Hartree-Fock
This module implements closed- and open-shell Hartree-Fock, both density-fitted (dfhf/dfuhf, via the @dfhf macro) and exact non-DF from AO integrals (hf/uhf, via the @hf/@uhf macros), sharing a common SCF loop with a pluggable Fock builder. Here's an example of computing density-fitted HF with the @dfhf macro:
using ElemCo
# Define the molecule
geometry="bohr
O 0.000000000 0.000000000 -0.130186067
H1 0.000000000 1.489124508 1.033245507
H2 0.000000000 -1.489124508 1.033245507"
basis = Dict("ao"=>"cc-pVDZ",
"jkfit"=>"cc-pvtz-jkfit",
"mpfit"=>"cc-pvdz-mpfit")
# Compute DF-HF
@dfhfThis code defines a water molecule, computes DF-HF using the cc-pVDZ basis set, and calculates the DF-HF energy.
Exported functions and types
ElemCo.HF.dfhf — Method
dfhf(EC::ECInfo)Perform closed-shell DF-HF calculation. If EC.fd.df3idx is set, uses pre-existing 3-index integrals (mmL) in MO basis (S=I). Returns the energy as the HF key in OutDict.
ElemCo.HF.dfhf_positron — Method
dfhf_positron(EC::ECInfo)Perform closed-shell DF-HF calculation with positron. Returns the energy as the HF key in OutDict.
ElemCo.HF.dfuhf — Method
dfuhf(EC::ECInfo)Perform DF-UHF calculation. If EC.fd.df3idx is set, uses pre-existing 3-index integrals (mmL/MML) in MO basis (S=I). Returns the energy as the UHF and HF keys in OutDict.
ElemCo.HF.hf — Method
hf(EC::ECInfo)Perform a closed-shell Hartree-Fock calculation directly from exact (non-density-fitted) AO integrals stored as scratch files (the ± supermatrix store, "S_AA", "h_AA", generated on demand via ensure_ao_integrals!); electron count etc. come from the molecular system.
The SCF is solved in the non-orthogonal AO basis using the overlap S_AA: canonical orthogonalization handles linearly-dependent basis sets, the Fock matrix is built exactly from the memory-mapped 4-index AO integrals (gen_fock), and the metric residual S·D·F − F·D·S drives convergence (shared loop scf_closed_shell!).
Returns the energy as the HF key in OutDict. The converged MO coefficients are written to the wavefunction dump for subsequent (AO→MO) correlation steps.
ElemCo.HF.uhf — Method
uhf(EC::ECInfo)Perform exact (non-density-fitted) unrestricted Hartree-Fock from AO integrals stored as scratch files (the ± supermatrix store, "S_AA", "h_AA", generated on demand via ensure_ao_integrals!). Uses the shared open-shell loop scf_open_shell! with a UHF Fock builder over the AO integrals (gen_ufock) and canonical orthogonalization for linear-dependence handling. Returns the energy as the UHF and HF keys in OutDict.
Internal functions
ElemCo.HF.canonical_orth_for_occ — Method
canonical_orth_for_occ(EC, sao; open_shell)Canonical orthogonalization of the AO overlap sao (handling linearly-dependent basis sets) together with the guard that enough orbitals survive to host the occupied space. Returns (Xorth, Xredundant).
ElemCo.HF.canonicalize_occ_virt! — Method
canonicalize_occ_virt!(ϵ, cMO, fock, occ, virt)Rotate cMO within the occupied and within the virtual space so that the orbitals diagonalize the corresponding blocks of the final Fock matrix, and set ϵ to the resulting orbital energies.
An SCF iteration builds the Fock matrix, tests convergence and breaks, so the orbitals the loop leaves behind diagonalize the (DIIS-extrapolated) Fock matrix of the PREVIOUS iteration — the final one keeps occ-occ/virt-virt off-diagonal elements of the order of the remaining orbital gradient (≈ 0.1·sqrt(scf.thr), so ~1e-6 at the default). That is enough for everything downstream to see non-canonical orbitals and pay for a pseudo-canonicalization: it is what trips the cc.fock_diag_thr check in (T).
Rotations within the occupied and within the virtual space leave the density — and therefore the energy, and the Fock matrix itself — exactly invariant, so this costs two small diagonalizations and cannot perturb the converged solution.
Orbitals in neither space — the linearly-dependent ones, which carry a sentinel energy and no meaningful Fock matrix elements — are left untouched.
ElemCo.HF.fock_expectation_energies — Method
fock_expectation_energies(fock, cMO)Orbital energies of a non-iterated SCF (scf.maxit=0): the diagonal of the Fock matrix in the basis of the given orbitals, $ϵ_p = ⟨p|F|p⟩$.
With no iteration the orbitals are never updated, so they do not diagonalize the Fock matrix and there are no eigenvalues to report. The expectation values are the meaningful stand-in — they are what a Fock matrix build for fixed orbitals can say about them, and they coincide with the eigenvalues when the orbitals happen to be canonical.
ElemCo.HF.kept_virtuals — Method
kept_virtuals(virt, nredundant::Int)The virtual orbitals excluding the nredundant linearly-dependent ones, which the solver appends as the highest orbitals with a sentinel energy (eigen_orth).
ElemCo.HF.redundant_orbital_classes — Method
redundant_orbital_classes(EC, Xredundant)Closed-shell orbital classes (with deleted/redundant orbitals flagged) for dump_orbitals, or nothing when no orbitals were removed.
ElemCo.HF.redundant_orbital_classes_uhf — Method
redundant_orbital_classes_uhf(EC, Xredundant)Open-shell analogue of redundant_orbital_classes: a per-spin (α, β) tuple of orbital classes, or nothing when no orbitals were removed.
ElemCo.HF.scf_closed_shell! — Method
scf_closed_shell!(EC, cMO, sao, hsmall, Enuc, fockbuilder, solver)Shared closed-shell SCF iteration loop, parameterized by a pluggable Fock builder and eigensolver. Used by both the density-fitted HF (dfhf) and the exact non-density-fitted HF (hf).
cMO(nAO × nMO) holds the orbital coefficients and is updated in place.saois the AO overlap (Ifor already-orthonormal MO integrals).hsmallis the 1-e (core) Hamiltonian,Enucthe constant energy offset.fockbuilder(cMO) -> fockbuilds the Fock matrix from the current orbitals.solver(fock) -> (ϵ, cMO_new)solves the (generalized) eigenvalue problem.
With scf.maxit=0 no iteration is performed: the Fock matrix and the energy are built for the given orbitals, which are left untouched (see fock_expectation_energies).
Returns (EHF, ϵ, fock) (the converged Fock matrix is returned for orbital dumps).
ElemCo.HF.scf_iterations — Method
scf_iterations(EC::ECInfo) -> (niter, noiter)Number of SCF passes to run and whether this is a no-iteration (scf.maxit=0) run.
scf.maxit=0 means "do not iterate", not "do nothing": the loop body still has to run once to build the Fock matrix and the energy for the given orbitals (noiter then makes the caller break before the orbitals are updated). Running it zero times would also leave the Fock matrix the loops return undefined.
ElemCo.HF.scf_open_shell! — Method
scf_open_shell!(EC, cMO::SpinMatrix, sao, h1a, h1b, Enuc, fockbuilder, solver)Shared unrestricted (open-shell) SCF loop with a pluggable Fock builder and solver, the open-shell analogue of scf_closed_shell!. cMO (α/β coefficients) is updated in place. fockbuilder(cMO) returns the spin Fock pair (indexable [1]/[2], e.g. a SpinMatrix); solver(fock_spin) returns (ϵ_spin, cMO_spin). h1a/h1b are the α/β core Hamiltonians (equal for UHF). Convergence is driven by the metric residual S·D·F − F·D·S per spin. scf.maxit=0 builds the Fock matrices for the given orbitals without iterating, as in the closed-shell loop. Returns (EHF, ϵ, fock) with ϵ an [ϵα, ϵβ] vector and fock the converged spin Fock pair.
ElemCo.HF.scf_thren — Method
scf_thren(EC)Energy-convergence threshold for the SCF: the user-set scf.thren if non-negative, otherwise a default derived from the residual threshold (sqrt(scf.thr)*0.1).
ElemCo.HF.starting_orbitals — Method
starting_orbitals(EC)Starting orbitals for the DF SCF (dfhf/dfuhf): previously saved orbitals if present, otherwise the configured scf.guess. Always returns a SpinMatrix; the caller reduces it to the closed-shell α block or unrestricts it as needed.