In [ ]:
import psi4
import forte
We need to generate SCF orbitals via psi4, so we will use some of the techniques used in Tutorial 1. However, we'll make a function
In [ ]:
def run_psi4(geom, basis = 'sto-3g', reference = 'rhf'):
# build the molecule object
mol = psi4.geometry(geom)
# set basis/options
psi4.set_options({'basis': basis, 'reference' : reference, 'scf_type': 'pk'})
# pipe output to the file output.dat
psi4.core.set_output_file('output.dat', False)
# run scf and return the energy and a wavefunction object (will work only if pass return_wfn=True)
E_scf, wfn = psi4.energy('scf', return_wfn=True)
psi4.core.clean()
return (E_scf, wfn)
and then get the energy and wavefunction using this function
In [ ]:
# setup xyz geometry
geom = """
O
H 1 1.0
H 1 1.0 2 180.0
"""
(E_scf, wfn) = run_psi4(geom)
print(f'SCF Energy = {E_scf}')
In later tutorials we will use the function forte.utils.psi4_scf(), which generalizes the one shown above to include the ability to pass options.
In [ ]:
forte.startup()
In [ ]:
from forte import forte_options
options = psi4.core.get_options() # options = psi4 option object
options.set_current_module('FORTE') # read options labeled 'FORTE'
forte_options.get_options_from_psi4(options)
A common first task when interacting with the Forte API is to compute 1- and 2-electron molecular integrals. The integral code needs to know if any orbitals will be dropped off from a computation. To do so we need to pass a tuple that specifies how many doubly occupied (docc) and unoccupied (uocc) orbitals to drop.
In [ ]:
# Setup forte and prepare the active space integral class
mos_spaces = {'FROZEN_DOCC' : [1,0,0,0,0,0,0,0], # freeze the O 1s orbital
'RESTRICTED_DOCC' : [1,0,0,0,0,1,0,0]}
mo_space_info = forte.make_mo_space_info_from_map(wfn,mos_spaces,[])
Task 1: Take a look at the file
forte/src/base_classes/mo_space_info.h. This class stores information about molecule orbital space. However, only one function is exposed. Create a github/forte pull request to expose functions that you need to find out the number of orbital in each space (including symmetry).
In [ ]:
mo_space_info.size('ACTIVE')
ForteIntegral object to read integrals from psi4In Forte there are two classes responsible for handling integrals:
ForteIntegral: reads the integrals from psi4 and stores them in varios formats (conventional, density fitting, Cholesky, ...).ActiveSpaceIntegrals: stores a copy of all integrals and it is used by active space methods. This class only stores a subset of the integrals and includes an effective potential due to non-active doubly occupied orbitals.We will first build the ForteIntegral object via the function make_forte_integrals
In [ ]:
ints = forte.make_forte_integrals(wfn, forte_options, mo_space_info)
print(f'Number of molecular orbitals: {ints.nmo()}')
print(f'Number of correlated molecular orbitals: {ints.ncmo()}')
Task 2: Take a look at the file
forte/src/integrals/integrals.h. This class allows the user to access the 1-/2-electron integrals via functions likedouble oei_a(size_t p, size_t q). However, these functions are not exposed to the python side. Expose one of these functions and commit them to github/forte.
ActiveSpaceIntegrals object to access integral elementsWe can now create an ActiveSpaceIntegrals object using the ForteIntegral object and a couple of extra bits of information stored in the MOSpaceInfo object. When we create this object we need to specify two orbital spaces:
ActiveSpaceIntegrals uses this information to create 1-/2-electron integrals for the active orbitals
In [ ]:
# the space that defines the active orbitals. We select only the 'ACTIVE' part
active_space = 'ACTIVE'
# the space(s) with non-active doubly occupied orbitals
core_spaces = ['RESTRICTED_DOCC']
as_ints = forte.make_active_space_ints(mo_space_info, ints, active_space, core_spaces)
In [ ]:
print(f'Frozen-core energy = {as_ints.frozen_core_energy()}')
print(f'Nuclear repulsion energy = {as_ints.nuclear_repulsion_energy()}')
print(f'Scalar energy = {as_ints.scalar_energy()}')
We can also access individual elements of the 1-/2-electron integrals. This class stores five types of integrals:
oei_a function.oei_b function.tei_aa function.tei_ab functiontei_bb functionHere we denote beta spin orbitals with a bar above the orbital index.
In [ ]:
print(f'<0|h|0> = {as_ints.oei_a(0,0)}')
print(f'<0a0a||0a0a> = {as_ints.tei_aa(0,0,0,0)}')
print(f'<0a0b||0a0b> = {as_ints.tei_ab(0,0,0,0)}')
We can also print all the integrals at once (see the output.dat file)
In [ ]:
as_ints.print()
In [ ]:
forte.cleanup()