Quick Start with MiniHF

Welcome to MiniHF, a lightweight Hartree-Fock quantum chemistry library. This notebook provides a comprehensive introduction to using MiniHF for molecular structure calculations. We’ll walk through the complete workflow of:

  1. Creating molecules with the Molecule class

  2. Building basis sets with the BasisSet class

  3. Constructing molecular Hamiltonians with the Hamiltonian class

  4. Solving Hartree-Fock equations with the HFSolver class

Let’s get started!

1. Creating a Molecule

The first step in any quantum chemistry calculation is to define the molecular system. In MiniHF, we use the Molecule class to represent molecules. Atoms can be specified using element symbols or atomic numbers, followed by their Cartesian coordinates in angstroms.

Let’s create a water molecule (H₂O) as our example system.

from minihf.molecule import Molecule

# Define atoms with coordinates (element symbol or atomic number + x, y, z in angstroms)
atoms = [
    'O 0.0, 0.0, 0.0',      # Oxygen at origin
    'H 0.0, -0.757, 0.587', # Hydrogen atom
    'H 0.0, 0.757, 0.587'   # Hydrogen atom
]

# Build the molecule (charge=0 for neutral molecule, multiplicity=1 for singlet)
mol = Molecule.build(atoms=atoms, charge=0, multiplicity=1)

print(f"Successfully created molecule: {mol}")
print(f"Number of atoms: {len(mol)}")
print(f"Molecular charge: {mol.charge}")
print(f"Multiplicity: {mol.multiplicity}")

# Print detailed atom information
print("\nAtom details:")
for i, atom in enumerate(mol):
    print(f"  Atom {i}: {atom.symbol} (Z={atom.number}) at {atom.coords}")
Successfully created molecule: Molecule(atoms=3, charge=0, multiplicity=1)
Number of atoms: 3
Molecular charge: 0
Multiplicity: 1

Atom details:
  Atom 0: O (Z=8) at [0. 0. 0.]
  Atom 1: H (Z=1) at [ 0.    -0.757  0.587]
  Atom 2: H (Z=1) at [0.    0.757 0.587]

2. Creating a Basis Set

A basis set is a set of mathematical functions (typically Gaussian-type orbitals) used to expand the molecular orbitals. MiniHF supports various standard basis sets through the BasisSet class.

We’ll use the 6-31G basis set, which is a popular split-valence basis set that provides a good balance between computational cost and accuracy for small molecules.

from minihf.basis import BasisSet

# Build basis set for the water molecule
basis = BasisSet.build(mol, '6-31g.nwchem')

# Let's examine the basis functions for the oxygen atom
print(f"Basis set created with {len(basis)} basis functions:")
for i, orb in enumerate(basis):
    print(f"  Orbital {i}: shell={orb.shell}, n_primitives={len(orb.exponents)}")
Basis set created with 13 basis functions:
  Orbital 0: shell=(0, 0, 0), n_primitives=6
  Orbital 1: shell=(0, 0, 0), n_primitives=3
  Orbital 2: shell=(1, 0, 0), n_primitives=3
  Orbital 3: shell=(0, 1, 0), n_primitives=3
  Orbital 4: shell=(0, 0, 1), n_primitives=3
  Orbital 5: shell=(0, 0, 0), n_primitives=1
  Orbital 6: shell=(1, 0, 0), n_primitives=1
  Orbital 7: shell=(0, 1, 0), n_primitives=1
  Orbital 8: shell=(0, 0, 1), n_primitives=1
  Orbital 9: shell=(0, 0, 0), n_primitives=3
  Orbital 10: shell=(0, 0, 0), n_primitives=1
  Orbital 11: shell=(0, 0, 0), n_primitives=3
  Orbital 12: shell=(0, 0, 0), n_primitives=1

3. Constructing the Molecular Hamiltonian

The molecular Hamiltonian contains all the necessary information to describe the quantum mechanical system. It includes:

  • Overlap matrix (S): Measures the overlap between basis functions

  • Kinetic energy matrix (T): Electron kinetic energy integrals

  • Potential energy matrix (V): Electron-nuclear attraction integrals

  • Core Hamiltonian (H_core): T + V, the one-electron Hamiltonian

  • Electron repulsion integrals (Eri): Two-electron Coulomb integrals

The Hamiltonian class automates the calculation of all these integrals.

from minihf.hamiltonian import Hamiltonian

# Build the Hamiltonian for our molecule
ham = Hamiltonian.build(mol, basis)

# Check the shapes of the integral matrices
n_bf = len(basis)
print(f"Number of basis functions: {n_bf}")
print(f"\nMatrix dimensions:")
print(f"  Overlap matrix (S): {ham['S'].shape}")
print(f"  Kinetic energy matrix (T): {ham['T'].shape}")
print(f"  Potential energy matrix (V): {ham['V'].shape}")
print(f"  Core Hamiltonian (H_core): {ham['Hcore'].shape}")
print(f"  Electron repulsion integrals (Eri): {ham['Eri'].shape}")
Number of basis functions: 13

Matrix dimensions:
  Overlap matrix (S): (13, 13)
  Kinetic energy matrix (T): (13, 13)
  Potential energy matrix (V): (13, 13)
  Core Hamiltonian (H_core): (13, 13)
  Electron repulsion integrals (Eri): (13, 13, 13, 13)

4. Solving the Hartree-Fock Equation

The Hartree-Fock method is the simplest self-consistent field (SCF) approach for solving the electronic structure problem. It approximates the many-electron wavefunction as a product of single-electron orbitals.

MiniHF’s HFSolver class implements the restricted Hartree-Fock (RHF) method for closed-shell molecules like water.

from minihf.scf.hf_solver import HFSolver

# Initialize the HF solver
hf_solver = HFSolver(ham)

# Solve the RHF equations
results = hf_solver.rhf()

# Extract key results
energy_total = results['energy_total']
energy_elec = results['energy_elec']
energy_nuc = results['energy_nuc']
mo_energies = results['mo_energy']

print("=== Hartree-Fock Calculation Results ===")
print(f"\nTotal energy: {energy_total:.6f} Hartree")
print(f"Electronic energy: {energy_elec:.6f} Hartree")
print(f"Nuclear repulsion energy: {energy_nuc:.6f} Hartree")

# Print molecular orbital energies
print("\nMolecular orbital energies (eV):")
for i, energy in enumerate(mo_energies):
    # Convert Hartree to eV (1 Hartree = 27.2114 eV)
    energy_eV = energy * 27.2114
    print(f"  MO {i+1}: {energy_eV:>8.4f} eV")

# Identify occupied and virtual orbitals (water has 10 electrons = 5 occupied MOs)
n_occupied = 5
print(f"\nOccupied orbitals: MO 1-{n_occupied}")
print(f"Virtual orbitals: MO {n_occupied+1}-{len(mo_energies)}")

# Calculate HOMO-LUMO gap
homo_energy = mo_energies[n_occupied - 1] * 27.2114
lumo_energy = mo_energies[n_occupied] * 27.2114
homo_lumo_gap = lumo_energy - homo_energy
print(f"\nHOMO energy: {homo_energy:.4f} eV")
print(f"LUMO energy: {lumo_energy:.4f} eV")
print(f"HOMO-LUMO gap: {homo_lumo_gap:.4f} eV")
=== Hartree-Fock Calculation Results ===

Total energy: -74.498108 Hartree
Electronic energy: -91.861400 Hartree
Nuclear repulsion energy: 17.363292 Hartree

Molecular orbital energies (eV):
  MO 1: -559.0048 eV
  MO 2: -47.4916 eV
  MO 3: -28.2713 eV
  MO 4: -17.2360 eV
  MO 5: -15.3793 eV
  MO 6:   7.0822 eV
  MO 7:   9.5516 eV
  MO 8:  28.3278 eV
  MO 9:  30.6858 eV
  MO 10:  31.8366 eV
  MO 11:  46.2353 eV
  MO 12:  62.6364 eV
  MO 13:  77.9588 eV

Occupied orbitals: MO 1-5
Virtual orbitals: MO 6-13

HOMO energy: -15.3793 eV
LUMO energy: 7.0822 eV
HOMO-LUMO gap: 22.4614 eV