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
t_ints = transform_integrals(kin, ne, er, orb)
# Transformed one-electron integrals: attribute 'one' (list)
(one,) = t_ints.one # or: one = t_ints.one[0]
# Transformed two-electron integrals: attribute 'two' (list)
(two,) = t_ints.two # or: two = t_ints.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
t_ints = transform_integrals(kin, ne, er, orba, orbb)
# One-electron integrals h_aa and h_bb
one_mo_alpha, one_mo_beta = t_ints.one
# Two-electron integrals <aa|aa>, <ab|ab>, and <bb|bb>
two_mo_alpha_alpha, two_mo_alpha_beta, two_mo_beta_beta = t_ints.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.