Orbitals and basis sets#

Electronic wave functions and molecular orbitals#

State vectors representing single-electron systems reside in a Hilbert space formed as a direct product

\[ \mathcal{V} = \mathcal{V}^\mathrm{o} \otimes \mathcal{V}^\mathrm{c} \otimes \mathcal{V}^\mathrm{s} \]

The underlying vector spaces are associated with orbital, charge, and spin degrees of freedom. The elements of \(\mathcal{V}\) can be expressed in terms of

\[\begin{split} \psi^{\mathrm{L}\alpha}(\mathbf{r},t) \otimes \begin{pmatrix} 1\\0 \end{pmatrix} \otimes \begin{pmatrix} 1\\0 \end{pmatrix}; \quad \psi^{\mathrm{L}\beta}(\mathbf{r},t) \otimes \begin{pmatrix} 1\\0 \end{pmatrix} \otimes \begin{pmatrix} 0\\1 \end{pmatrix} \end{split}\]
\[\begin{split} \psi^{\mathrm{S}\alpha}(\mathbf{r},t) \otimes \begin{pmatrix} 0\\1 \end{pmatrix} \otimes \begin{pmatrix} 1\\0 \end{pmatrix} ; \quad \psi^{\mathrm{S}\beta}(\mathbf{r},t) \otimes \begin{pmatrix} 0\\1 \end{pmatrix} \otimes \begin{pmatrix} 0\\1 \end{pmatrix} \end{split}\]

with a sum that equals a general wave function for a single-electron system, also known as a spinor

\[\begin{split} \psi(\mathbf{r},t) = \begin{pmatrix} \psi^{\mathrm{L}\alpha} \\ \psi^{\mathrm{L}\beta} \\ \psi^{\mathrm{S}\alpha} \\ \psi^{\mathrm{S}\beta} \end{pmatrix} \end{split}\]

In this general case, the particle density becomes

\[ n(\mathbf{r},t) = \psi^\dagger \psi = \sum_\mathrm{c}^{\mathrm{L,S}} \sum_\sigma^{\alpha,\beta} \left| \psi^{\mathrm{c}\sigma}(\mathbf{r},t) \right|^2 \]

Spinor components \(\psi^{\mathrm{S}\alpha}\) and \(\psi^{\mathrm{S}\beta}\) are small and sometimes ignored, reducing the electronic wave function to its two-component form

\[\begin{split} \psi(\mathbf{r},t) = \begin{pmatrix} \psi^{\mathrm{L}\alpha} \\ \psi^{\mathrm{L}\beta} \end{pmatrix} \end{split}\]

For a system described by a spin-independent Hamiltonian, the spatial and spin degrees of freedom are separable and we arrive at (suppressing the time variable) single-electron wave functions, or spin orbitals, of the form

\[\begin{split} \psi(\mathbf{r}) = \phi(\mathbf{r}) \sigma ; \quad \sigma = c_\alpha \begin{pmatrix} 1 \\ 0 \end{pmatrix} + c_\beta \begin{pmatrix} 0 \\ 1 \end{pmatrix} \end{split}\]

In the collinear approximation, all spin orbitals are eigenfunctions of the spin operator \(\hat{s}_z\) such that every spatial function (or molecular orbital), \(\phi(\mathbf{r})\), gives rise to two spin orbitals

\[\begin{split} \psi_{p}(\mathbf{r}) = \phi_p(\mathbf{r}) \begin{pmatrix} 1 \\ 0 \end{pmatrix} ; \quad \psi_{\bar{p}}(\mathbf{r}) = \phi_p(\mathbf{r}) \begin{pmatrix} 0 \\ 1 \end{pmatrix} \end{split}\]

referred to as \(\alpha\)- and \(\beta\)-spin orbitals, respectively.

Linear combination of atomic orbitals#

In quantum chemistry, molecular orbitals (MOs) are normally expanded in a set of atom-centered basis functions, or localized atomic orbitals (AOs)

\[ \phi_p(\mathbf{r}) = \sum_\alpha c_{\alpha p} \chi_\alpha(\mathbf{r} - \mathbf{R}_\alpha) \]

where \(\mathbf{R}_\alpha\) denotes the atomic position center of basis function \(\chi_\alpha\), and the expansion coefficients \(c_{\alpha p}\) are known as molecular orbital (MO) coefficients. Using the Dirac notation, this linear combination of atomic orbitals (LCAO) expansion takes the form

\[ | \phi_p \rangle = \sum_\alpha | \chi_\alpha \rangle c_{\alpha p} \]

It can be convenient to collect the set of MOs (and AOs) as a row vector and introduce a notation

\[ | \overline{\phi} \rangle = (\ldots, | \phi_p \rangle, \ldots) \]

The LCAO expansion for the entire set of MOs can then be compactly written as

\[ | \overline{\phi} \rangle = | \overline{\chi} \rangle \mathbf{C} \]

where \(\mathbf{C}\) is the MO coefficient matrix.

Ordering of atomic orbitals#

The VeloxChem program orders AO basis functions based on

  1. Angular momentum quantum number, \(l\)

  2. Angular momentum quantum number, \(m_l\)

  3. User-defined order of atoms

Considering carbon monoxide using a minimal basis set

import veloxchem as vlx

mol_str = """2

C        0.00000000    0.00000000   -0.71500000
O        0.00000000    0.00000000    0.71500000
"""
molecule = vlx.Molecule.read_xyz_string(mol_str)
basis = vlx.MolecularBasis.read(molecule, "sto-3g", ostream=None)

scf_drv = vlx.ScfRestrictedDriver()
scf_drv.ostream.mute()
scf_results = scf_drv.compute(molecule, basis)
molecule.show()

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
jupyter labextension install jupyterlab_3dmol

The order of basis functions become

\(\chi_{1s}^{C}\), \(\chi_{2s}^{C}\), \(\chi_{1s}^{O}\), \(\chi_{2s}^{O}\) \(\chi_{2py}^{C}\), \(\chi_{2py}^{O}\), \(\chi_{2pz}^{C}\), \(\chi_{2pz}^{O}\), \(\chi_{2px}^{C}\), \(\chi_{2px}^{O}\)

And the resulting MO coefficients are available as

import numpy as np

np.set_printoptions(precision=3, suppress=True, linewidth=170)

C = scf_results["C_alpha"]
print("MO coefficient matrix:\n", C)
MO coefficient matrix:
 [[-0.001 -0.994 -0.091 -0.228 -0.109  0.     0.     0.    -0.     0.105]
 [ 0.004 -0.024  0.26   0.836  0.47  -0.    -0.    -0.     0.    -0.536]
 [-0.995 -0.    -0.231  0.114 -0.036  0.     0.    -0.     0.    -0.089]
 [-0.023  0.005  0.856 -0.526  0.196 -0.    -0.     0.    -0.     0.547]
 [ 0.     0.     0.     0.     0.     0.001  0.431 -0.012 -0.912 -0.   ]
 [ 0.    -0.     0.    -0.     0.     0.002  0.847  0.007  0.548  0.   ]
 [ 0.004 -0.004  0.141 -0.003 -0.621  0.     0.    -0.     0.    -0.942]
 [ 0.003 -0.002 -0.112 -0.38   0.578 -0.    -0.    -0.     0.    -0.852]
 [ 0.     0.     0.    -0.     0.     0.431 -0.001 -0.912  0.012  0.   ]
 [ 0.    -0.    -0.     0.     0.     0.847 -0.002  0.548 -0.007 -0.   ]]

Overlap matrix#

The overlap between two basis functions is

\[ S_{\alpha\beta} = \langle \chi_\alpha | \chi_\beta \rangle \]

and the overlap matrix can be written as

\[ \mathbf{S} = \langle \overline{\chi} | \overline{\chi} \rangle \]

where

\[ \langle \overline{\chi} | = (\ldots, \langle \chi_\alpha |, \ldots)^T \]
S = scf_results["S"]
print("Overlap matrix:\n", S)
Overlap matrix:
 [[ 1.     0.248  0.     0.017  0.     0.     0.    -0.031  0.     0.   ]
 [ 0.248  1.     0.022  0.255  0.     0.     0.    -0.251  0.     0.   ]
 [ 0.     0.022  1.     0.237  0.     0.     0.036  0.     0.     0.   ]
 [ 0.017  0.255  0.237  1.     0.     0.     0.337  0.     0.     0.   ]
 [ 0.     0.     0.     0.     1.     0.132  0.     0.     0.     0.   ]
 [ 0.     0.     0.     0.     0.132  1.     0.     0.     0.     0.   ]
 [ 0.     0.     0.036  0.337  0.     0.     1.    -0.285  0.     0.   ]
 [-0.031 -0.251  0.     0.     0.     0.    -0.285  1.     0.     0.   ]
 [ 0.     0.     0.     0.     0.     0.     0.     0.     1.     0.132]
 [ 0.     0.     0.     0.     0.     0.     0.     0.     0.132  1.   ]]

Basis sets of atomic orbitals#

Assign to molecule#

A basis set object associated with a molecule can be created with the read method.

basis = vlx.MolecularBasis.read(molecule, "cc-pVDZ", ostream=None)

Basis set library#

Our software comes distributed with several of the standard basis sets and families of basis sets. For a given element, the available basis sets in the VeloxChem library can be provided with the get_avail_basis. Should a basis set not be included in the software distribution, it can be readily supplied by the user in a separate file in the working directory, with basis set parameters available from, e.g., Basis Set Exchange.

print("Available basis sets for carbon:\n", basis.get_avail_basis("C"))
Available basis sets for carbon:
 ['6-31++G', '6-31++G*', '6-31++G**', '6-31+G', '6-31+G*', '6-31+G**', '6-311++G', '6-311++G(2D,2P)', '6-311++G(3DF,3PD)', '6-311++G*', '6-311++G**', '6-311+G', '6-311+G(2D,P)', '6-311+G*', '6-311+G**', '6-311G', '6-311G(2DF,2PD)', '6-311G*', '6-311G**', '6-31G', '6-31G(2DF,P)', '6-31G(3DF,3PD)', '6-31G*', '6-31G**', 'ANO-L-MB', 'ANO-L-VDZP', 'ANO-L-VQZP', 'ANO-L-VTZP', 'ANO-S-MB', 'ANO-S-VDZP', 'AO-START-GUESS', 'AUG-CC-PCV5Z', 'AUG-CC-PCVDZ', 'AUG-CC-PCVQZ', 'AUG-CC-PCVTZ', 'AUG-CC-PV5Z', 'AUG-CC-PV6Z', 'AUG-CC-PVDZ', 'AUG-CC-PVQZ', 'AUG-CC-PVTZ', 'AUG-PCSEG-0', 'AUG-PCSEG-1', 'AUG-PCSEG-2', 'AUG-PCSEG-3', 'AUG-PCSEG-4', 'AUG-PCX-1', 'AUG-PCX-2', 'AUG-PCX-3', 'AUG-PCX-4', 'CC-PCV5Z', 'CC-PCVDZ', 'CC-PCVQZ', 'CC-PCVTZ', 'CC-PV5Z', 'CC-PV6Z', 'CC-PVDZ', 'CC-PVQZ', 'CC-PVTZ', 'DAUG-CC-PCV5Z', 'DAUG-CC-PCVDZ', 'DAUG-CC-PCVQZ', 'DAUG-CC-PCVTZ', 'DAUG-CC-PV5Z', 'DAUG-CC-PV6Z', 'DAUG-CC-PVDZ', 'DAUG-CC-PVQZ', 'DAUG-CC-PVTZ', 'DEF2-QZVP', 'DEF2-QZVPD', 'DEF2-QZVPP', 'DEF2-QZVPPD', 'DEF2-SV(P)', 'DEF2-SVP', 'DEF2-SVPD', 'DEF2-TZVP', 'DEF2-TZVPD', 'DEF2-TZVPP', 'DEF2-TZVPPD', 'PCSEG-0', 'PCSEG-1', 'PCSEG-2', 'PCSEG-3', 'PCSEG-4', 'PCX-1', 'PCX-2', 'PCX-3', 'PCX-4', 'SADLEJ-PVTZ', 'STO-3G', 'STO-3G-OLD', 'STO-6G', 'TAUG-CC-PCV5Z', 'TAUG-CC-PCVDZ', 'TAUG-CC-PCVQZ', 'TAUG-CC-PCVTZ', 'TAUG-CC-PV5Z', 'TAUG-CC-PV6Z', 'TAUG-CC-PVDZ', 'TAUG-CC-PVQZ', 'TAUG-CC-PVTZ']

Table: Categories of basis set.

Category

Character

Example

Minimal

Single basis function per occupied atomic orbital, size for second-row elements is \([2s1p]\)

STO-3G

Double-\(\zeta\)

Two basis functions per occupied valence atomic orbital, size for second-row elements is \([3s2p]\)

6-31G

Triple-\(\zeta\)

Three basis functions per occupied valence atomic orbital, for second-row elements \([4s3p]\)

6-311G

Polarization

Basis functions with higher angular momenta; important for chemical bonding; size for second-row elements is \([3s2p1d]\)

cc-pVDZ

Diffuse

Basis functions with small exponents; important for excited states; sizes for second-row elements are \([4s3p]\) for 6-31+G and \([4s3p2d]\) for aug-cc-pVDZ

6-31+G, aug-cc-pVDZ

Number of basis functions#

The number of contracted and primitive basis functions are available from the get_dimensions_of_basis and get_dimensions_of_primitive_basis methods, respectively.

nbas = basis.get_dimensions_of_basis(molecule)
nprim = basis.get_dimensions_of_primitive_basis(molecule)

print("Number of contracted basis functions:", nbas)
print("Number of primitive basis functions:", nprim)
Number of contracted basis functions: 28
Number of primitive basis functions: 68

Additional information#

The molecular basis set label is retrieved with the get_label method:

label = basis.get_label()
print("Basis set label:", label)
Basis set label: CC-PVDZ

and additional basis set information is available:

print(basis.get_string(label, molecule))
Molecular Basis (CC-PVDZ)
===========================

Basis: CC-PVDZ                                              

  Atom Contracted GTOs           Primitive GTOs                

  O   (3S,2P,1D)                (17S,4P,1D)                   
  C   (3S,2P,1D)                (17S,4P,1D)                   

Contracted Basis Functions : 28                             
Primitive Basis Functions  : 68