PySCF (Python-based Simulations of Chemistry Framework) — modular pure-Python quantum chemistry library. Supports HF, DFT, MP2, CCSD, CCSD(T), CASSCF, TDDFT, geometry optimization, PES scanning, and spectroscopy. Based on pyscf.org documentation.
Resources
4Install
npx skillscat add silico-quantum/quantum-chem-skills/pyscf Install via the SkillsCat registry.
PySCF — Python-based Simulations of Chemistry Framework
Overview
PySCF is an open-source pure-Python quantum chemistry package featuring:
- Wave function methods: HF, MP2, MP3, CCSD, CCSD(T), CASSCF, CASCI, NEVPT2, DMRG
- DFT: All standard functionals (LDA, GGA, meta-GGA, hybrid, range-separated)
- Excited states: TDDFT, TDA, CIS, SF-DFT, EOM-CCSD
- Geometry optimization: Energy minimization, transition states, numerical Hessians
- PES scanning: Rigid scans, relaxed 1D/2D scans
- Spectroscopy: UV-Vis absorption, emission, CD, Raman
- Periodic systems: PBC-DFT, PBC-TDDFT
Installation: pip install pyscf
Core Concepts
Molecule Definition
from pyscf import gto
mol = gto.M(
atom='H 0 0 0; F 0 0 1.0', # Inline coordinates
# atom=open('h2o.xyz').read(), # Or read from file
basis='cc-pvdz', # Basis set
charge=0, # Molecular charge
spin=0, # Spin multiplicity (0=closed-shell singlet)
verbose=4 # Output level
)Basis Set Selection
| Task | Recommended Basis |
|---|---|
| Rapid screening | sto-3g, 3-21G |
| Balanced accuracy | 6-31G*, cc-pVDZ |
| High accuracy | cc-pVTZ, cc-pVQZ, def2-TZVP |
| Heavy atoms | LANL2DZ, Stuttgart RSC |
| Core excitations | cc-pCVTZ |
Common Functionals
# GGA functionals
'pbe', 'blyp', 'bp86', 'pbesol'
# Meta-GGA functionals
'scan', 'tpss', 'm06-l'
# Hybrid functionals
'b3lyp', 'pbe0', 'm06', 'm06-2x', 'wb97x', 'wb97x-d', 'wb97x-d3bj'
# Range-separated functionals (best for CT/excited states)
'wb97x', 'wb97x-d', 'wb97x-d3bj', 'camb3lyp', 'lc-wpbe'
# Dispersion correction
'b3lyp-d3', 'pbe-d3', 'wb97x-d3bj'Running Modes
mf = scf.RHF(mol)
mf.kernel() # Explicit run
mf.run() # Same as above
mf.converged # Check convergence
mf.e_tot # Total energy
# Progressive options
mf = scf.RHF(mol).newton() # Newton-Raphson acceleration
mf = scf.RHF(mol).density_fit() # Density fitting (faster)
mf = scf.RHF(mol).x2c() # Scalar relativistic (2-component)1. Wave Function Methods
1.1 Hartree-Fock (HF)
Restricted closed-shell RHF:
from pyscf import scf, gto
mol = gto.M(atom='H 0 0 0; F 0 0 1.0', basis='cc-pvdz')
mf = scf.RHF(mol).run()
print('E(RHF) =', mf.e_tot)Open-shell UHF/ROHF:
mol = gto.M(atom='O 0 0 0; O 0 0 1.2', spin=2, basis='cc-pvdz') # O2 triplet
mf = scf.UHF(mol).run() # Unrestricted
mf = scf.ROHF(mol).run() # Restricted open-shellDirac-Hartree-Fock (relativistic):
from pyscf import scf
mol = gto.M(atom='Hg 0 0 0', basis='cc-pvdz')
mf = scf.DHF(mol).run()1.2 MP2 (Second-Order Møller-Plesset)
from pyscf import mp, scf, gto
mol = gto.M(atom='C 0 0 0; O 0 0 1.2', basis='cc-pvdz')
mf = scf.RHF(mol).run()
# Closed-shell MP2
mmp = mp.MP2(mf).run()
print('E(MP2) =', mmp.e_tot)
print('E(corr) =', mmp.e_corr) # Correlation energy
print('T2 shape:', mmp.t2.shape)
# Open-shell UMP2
mmp = mp.UMP2(mf).run()1.3 CCSD / CCSD(T)
CCSD (Coupled-Cluster Singles and Doubles):
from pyscf import cc, scf, gto
mol = gto.M(atom='N 0 0 0; N 0 0 1.1', basis='cc-pvdz')
mf = scf.RHF(mol).run()
mycc = cc.CCSD(mf).run()
print('E(CCSD) =', mycc.e_tot)
print('T1 shape:', mycc.t1.shape)
print('T2 shape:', mycc.t2.shape)
# One-particle density matrix
dm = mycc.make_rdm1()CCSD(T) (perturbative triples):
et = cc.CCSD(mf).ccsd_t() # Returns ΔE
e_total = mycc.e_tot + etUnrestricted UCCSD:
mol = gto.M(atom='N 0 0 0', spin=1, basis='cc-pvdz')
mf = scf.UHF(mol).run()
mycc = cc.UCCSD(mf).run()1.4 CASSCF / CASCI
CASSCF (Complete Active Space SCF):
from pyscf import mcscf, scf, gto
mol = gto.M(atom='C 0 0 0; O 0 0 1.2', basis='cc-pvdz')
mf = scf.RHF(mol).run()
# CAS(nelec, ncas) — 10 electrons, 8 orbital active space
mc = mcscf.CASSCF(mf, 8, 10).run()
print('E(CASSCF) =', mc.e_tot)
print('CAS orbitals:', mc.mo_coeff.shape)
# Custom active space (manual orbital selection)
mc = mcscf.CASSCF(mf, 8, 10)
mo = mcscf.sort_mo(mc, mf.mo_coeff, [15, 16, 17, 20, 21, 22, 25, 26])
mc.run(mo)CASCI:
mc = mcscf.CASCI(mf, 8, 10).run()CASPT2 (multi-state second-order perturbation):
mc = mcscf.CASSCF(mf, 8, 10).run()
mcpt2 = mcscf.CASPT2(mc).run()
print('E(CASPT2) =', mcpt2.e_tot)NEVPT2 (N-Electron Valence Second-Order PT):
mcpt2 = mcscf.NEVPT2(mc).run()2. DFT Methods
2.1 Basic DFT
from pyscf import dft
mol = gto.M(atom='O 0 0 0; H 0 0 1.8; H 0 0.8 2.7', basis='cc-pvdz')
# RKS = Restricted Kohn-Sham (closed-shell)
mf = dft.RKS(mol, xc='pbe').run()
print('E(PBE) =', mf.e_tot)
mf = dft.RKS(mol, xc='b3lyp').run()
print('E(B3LYP) =', mf.e_tot)
# UKS = Unrestricted Kohn-Sham (open-shell)
mol = gto.M(atom='O 0 0 0', spin=2, basis='cc-pvdz')
mf = dft.UKS(mol, xc='pbe').run()2.2 Functional Selection Guide
xc_options = {
# LDA
'lda': 'svwn5', # VWN5 Slater-VWN (RPA)
# GGA
'pbe': 'pbe', # Perdew-Burke-Ernzerhof
'blyp': 'blyp', # Becke-Lee-Yang-Parr
'bp86': 'bp86', # B88 + P86
# Meta-GGA
'scan': 'scan', # Strongly Constrained
# Hybrid
'b3lyp': 'b3lyp', # Most popular
'pbe0': 'pbe0', # 25% HF exchange
'm06': 'm06', # Minnesota 2006
'm06-2x': 'm06-2x', # 54% HF exchange
'wb97x': 'wb97x', # Range-separated
'wb97x-d3': 'wb97x-d3bj', # with dispersion
# Dispersion correction
'pbe-d3': 'pbe,pbe-d3bj',
'b3lyp-d3': 'b3lyp,b3lyp-d3bj',
}2.3 Range-Separated Functionals
# ωB97X-D: Long-range corrected, ideal for CT and charge-transfer states
mf = dft.RKS(mol, xc='wb97x-d').run()
# CAM-B3LYP: Variable range separation
mf = dft.RKS(mol, xc='camb3lyp').run()2.4 Orbital Visualization / DOS
# MO coefficients and energies
mo_coeff = mf.mo_coeff
mo_energy = mf.mo_energy # in Hartree
# Mulliken population
from pyscf import lo
occ = lo.orth_ao(mol, 'minao')
dm = mf.make_rdm1()
mulliken = lo.mulliken(mol, dm, occ)3. Excited States: TDDFT / TDA
3.1 TDDFT (Linear Response)
from pyscf import tddft, dft, gto
mol = gto.M(atom='C 0 0 0; N 0 0 1.2', basis='cc-pvdz')
mf = dft.RKS(mol, xc='pbe').run()
# TDDFT calculation
td = tddft.TDDFT(mf).run()
print('Excitation energies (eV):', td.e * 27.2114)
print('Oscillator strengths:', td.f)
print('S1 energy (eV):', td.e[0] * 27.2114)
# Natural transition orbitals (NTO)
td.analyze()3.2 TDA (Tamm-Dancoff Approximation)
# TDA is faster than TDDFT but less accurate for double excitations
td = tddft.TDA(mf).run(nstates=5) # default: 10 states
td.analyze()3.3 UV-Vis Spectrum
import numpy as np
import matplotlib.pyplot as plt
# Compute 50 excited states
td = tddft.TDDFT(mf).run(nstates=50)
# Broadened spectrum
e = td.e * 27.2114 # eV
f = td.f
sigma = 0.1 # eV
energies = np.linspace(0, 10, 500)
spectrum = np.zeros_like(energies)
for ei, fi in zip(e, f):
spectrum += fi * np.exp(-(energies - ei)**2 / (2 * sigma**2))
plt.plot(energies, spectrum)
plt.xlabel('Energy (eV)')
plt.ylabel('Oscillator strength')
plt.show()3.4 Open-Shell TDDFT
mol = gto.M(atom='O 0 0 0', spin=2, basis='cc-pvdz')
mf = dft.UKS(mol, xc='pbe').run()
td = tddft.TDDFT(mf).run(nstates=5)3.5 SF-DFT (Spin-Flip DFT, for diradicals)
from pyscf import tddft, dft
mf = dft.RKS(mol, xc='b3lyp').run()
td = tddft.TDDFT(mf).run(nstates=3)
from pyscf import df
td_sf = df.DFTStateTransfer(mf, ['4.0', '5.0']) # energy gap range4. Geometry Optimization
4.1 Energy Minimization
from pyscf import dft, geomopt
mol = gto.M(atom='O 0 0 0; H 0 0 1.8; H 0 0.8 2.7', basis='cc-pvdz')
mf = dft.RKS(mol, xc='pbe').run()
# Standard optimization
opt = geomopt.GeometryOptimizer(mf)
mol_opt = opt.run()
print('Optimized coordinates:')
for i in range(mol_opt.natm):
print(mol_opt.atom_coord(i))
# Direct method
mol_opt = dft.RKS(mol, xc='b3lyp').geomopt().run()4.2 Transition State Optimization
from pyscf import dft, geomopt
mol = gto.M(atom='H 0 0 0; Cl 0 0 1.7', basis='cc-pvdz')
mf = dft.RKS(mol, xc='pbe').run()
# Dimer method for transition states
opt = geomopt.DimerOptimizer(mf).run()4.3 Frequency Calculation (Verify Minima / TS)
from pyscf import hessian
mol = gto.M(atom='O 0 0 0; H 0 0 1.8; H 0 0.8 2.7', basis='cc-pvdz')
mf = dft.RKS(mol, xc='pbe').run()
hess = hessian.RHF(mf).run()
# Frequencies
freq = hess.kernel()
print('Frequencies (cm^-1):', freq)5. Potential Energy Surface (PES) Scanning
5.1 Relaxed Scan (Stepwise Optimization)
from pyscf import dft
import numpy as np
mol = gto.M(atom='C 0 0 0; H 0 0 1.1', basis='cc-pvdz')
mf = dft.RKS(mol, xc='pbe').run()
# Scan C-H bond length
scan_coords = []
scan_energies = []
for r in np.linspace(0.9, 1.3, 10):
mol_temp = gto.M(
atom='C 0 0 0; H 0 0 %.4f' % r,
basis='cc-pvdz'
)
mf_temp = dft.RKS(mol_temp, xc='pbe').run(chkfile=False)
scan_coords.append(r)
scan_energies.append(mf_temp.e_tot)
print('Scan complete')
for c, e in zip(scan_coords, scan_energies):
print(f'r={c:.3f} E={e:.6f}')5.2 2D Relaxed Scan
# Combine two internal coordinates in nested loops6. Spectroscopy
6.1 UV-Vis Absorption Spectrum
import numpy as np
import matplotlib.pyplot as plt
def uv_vis_spectrum(td, nstates=50, sigma=0.05, emin=0, emax=10):
"""Generate broadened UV-Vis spectrum"""
e = td.e[:nstates] * 27.2114 # eV
f = td.f[:nstates]
energies = np.linspace(emin, emax, 1000)
absorption = np.zeros_like(energies)
for ei, fi in zip(e, f):
if fi > 0:
absorption += fi * np.exp(-(energies - ei)**2 / (2 * sigma**2))
return energies, absorption
mol = gto.M(atom='C 0 0 0; N 0 0 1.2', basis='cc-pvdz')
mf = dft.RKS(mol, xc='b3lyp').run()
td = tddft.TDDFT(mf).run(nstates=50)
e, spec = uv_vis_spectrum(td, sigma=0.05)
plt.plot(e, spec)
plt.xlabel('Photon energy (eV)')
plt.ylabel('Absorption (arb. units)')
plt.show()6.2 Emission Spectrum (from S1)
from pyscf import tddft, dft
mol = gto.M(atom='C 0 0 0; N 0 0 1.2', basis='cc-pvdz')
mf = dft.RKS(mol, xc='pbe').run()
td = tddft.TDDFT(mf).run(nstates=5)
# S1 emission = S1 → S0 vertical transition
td.emit() # Compute emission (experimental feature)6.3 CD Spectrum (Circular Dichroism)
from pyscf import tddft, gdft
mol = gto.M(atom='C 0 0 0; N 0 0 1.2', basis='cc-pvdz')
mf = gdft.RKS(mol, xc='pbe').run() # Use GDFT for CD
td = tddft.TDDFT(mf).run(nstates=20)
# Rotatory strength
td.analyze()7. Parallelization & Performance
7.1 Density Fitting (DF)
from pyscf import df, scf
mol = gto.M(atom='C 0 0 0; O 0 0 1.2', basis='def2-tzvp')
mf = scf.RHF(mol).density_fit(auxbasis='def2-tzvp-ri').run()7.2 Multi-threaded BLAS
import os
os.environ['OMP_NUM_THREADS'] = '16'
# Or at runtime
from pyscf.lib import num_threads
num_threads(16)7.3 CHK Files (Checkpoint / Restart)
mf = scf.RHF(mol)
mf.chkfile = 'hf.chk'
mf.run()
# Reload
mf = scf.RHF(mol)
mf.init_guess = 'chk'
mf.run(chkfile='hf.chk')8. Common Basis Sets
| Basis Set | Description | Use Case |
|---|---|---|
sto-3g |
Minimal basis, fastest | Quick testing |
3-21G |
Split-valence | Small molecule screening |
6-31G* |
Double-zeta + polarization | Standard organic molecules |
cc-pVDZ |
Correlation-consistent DZ | MP2/CCSD |
cc-pVTZ |
Correlation-consistent TZ | High precision |
def2-TZVP |
Pseudopotential basis | Heavy atoms |
LANL2DZ |
Effective core potential | Transition metals |
9. Troubleshooting
Q: SCF not converging?
# Method 1: Change initial guess
mf.init_guess = 'atom' # Atomic density
mf.init_guess = 'hcore' # Core Hamiltonian
mf.diis = True # DIIS acceleration (default on)
# Method 2: Newton-Raphson
mf = scf.RHF(mol).newton().run()
# Method 3: Change SCF method
mf = scf.DHF(mol).run() # Dirac-HF (heavy atoms)Q: Out of memory?
# Use density fitting to reduce memory
mf = scf.RHF(mol).density_fit().run()
# Use direct SCF
mf.direct_scf = TrueQ: How to print all orbital energies?
mf = scf.RHF(mol).run()
print('HOMO:', mf.mo_energy[mf.mo_occ > 0].max())
print('LUMO:', mf.mo_energy[mf.mo_occ == 0].min())Tools Script Index
| Script | Description |
|---|---|
tools/scf.py |
HF/DFT basic framework |
tools/tddft.py |
TDDFT/TDA calculation |
tools/dft.py |
DFT functional selection & comparison |
tools/mp2.py |
MP2 (RMP2/UMP2) |
tools/ccsd.py |
CCSD/CCSD(T) framework |
tools/cascf.py |
CASSCF/CASPT2/NEVPT2 framework |
tools/geometry.py |
Geometry optimization, TS, frequencies |
tools/spectrum.py |
UV-Vis/CD spectrum generation |
tools/pes.py |
1D/2D potential energy surface scanning |
tools/analysis.py |
Wavefunction analysis (Mulliken, Löwdin, DOS, NBO) |