2.5. The AO/MO Transformation of the Hamiltonian

All post-HF methods in PyBEST are implemented for the (orthonormal) molecular orbital basis, while the Hamiltonian used in SCF calculations is expressed in the atomic orbital basis (see Computing the matrix representation of the Hamiltonian). The AO/MO transformation is handled in all post-HF methods internally and the one- and two-electron integrals do not need to be transformed explicitly. If, however, an AO/MO transformation of the Hamiltonian is required, PyBEST provides the transform_integrals() function that transforms both one- and two-electron integrals to the MO basis.

Note

transform_integrals() transforms the whole Hamiltonian to the MO basis. If only a subspace of the Hamiltonian has to be transformed, the split_core_active() function has to be used. See Defining an Active Space Hamiltonian for more details on active space Hamiltonians.

The transform_integrals() function requires all one- and two-electron integrals as input argument in any order. All one-electron terms will be collected and combined in one final transformed one-electron integral. The individual terms are defined in Computing the matrix representation of the Hamiltonian. The transform_integrals() function is written in a general way so that it supports both restricted and unrestricted orbitals.

2.5.1. Transform integrals for restricted orbitals

For restricted orbitals, the transformed one- and two-electron integrals are equivalent for alpha and beta orbitals and only one set of MO integrals is returned. Specifically, transform_integrals() returns an instance of the IOData container, where the transformed one- and two-electron integrals (expressed in the MO basis) are stored as a list in the attribute one and two (see also Input/Output Operations: the IOData Container for more details on the IOData container).

# transform integrals for restricted orbitals orb
ti_ = transform_integrals(kin, ne, er, orb)

# transformed one-electron integrals: attribute 'one' (list)
(one,) = ti_.one   # or: one = ti_.one[0]
# transformed two-electron integrals: attribute 'two' (list)
(two,) = ti_.two   # or: two = ti_.two[0]

where kin and na (er) are the one- (two-)electron integrals expressed in the AO basis, orb are the molecular orbitals (for instance, the optimized canonical restricted HF orbitals).

Note

Instead of passing the orbitals, we can also pass some IOData container (or a return value of some method) that contains some molecular orbitals.

2.5.2. Transform integrals for unrestricted orbitals

In the case of unrestricted orbitals, the corresponding attributes of the IOData container returned by the transform_integrals() function form a list of 2 sets of one-electron integrals and 3 sets of two-electron integrals. The former correspond to the alpha-alpha and beta-beta blocks of the Hamiltonian, while the latter contains all possible spin combinations (alpha-alpha, alpha-beta, and beta-beta). Furthermore, for the unrestricted case, we have to pass both alpha (first argument) and beta (second argument) spin orbitals.

# transform integrals for unrestricted orbitals orba and orbb
ti_ = transform_integrals(kin, ne, er, orba, orbb)

# one-electron integrals h_aa and h_bb
one_mo_alpha, one_mo_beta = ti_.one
# two-electron integrals <aa|aa>, <ab|ab>, and <bb|bb>
two_mo_alpha_alpha, two_mo_alpha_beta, two_mo_beta_beta = ti_.two

Similarly, an IOData container can be passed that contains both alpha (stored as orb_a attribute) and beta (stored as orb_b attribute) orbitals instead of passing both orbitals explicitly.