def test_sparse_ci2(): import math import psi4 import forte import itertools import numpy as np import pytest from forte import forte_options ref_fci = -5.623851783330647 psi4.core.clean() # need to clean the options otherwise this job will interfere forte.clean_options() h2o = psi4.geometry(""" He He 1 1.0 """) psi4.set_options({'basis': 'cc-pVDZ'}) _, wfn = psi4.energy('scf', return_wfn=True) na = wfn.nalpha() nb = wfn.nbeta() nirrep = wfn.nirrep() wfn_symmetry = 0 forte.startup() forte.banner() psi4_options = psi4.core.get_options() psi4_options.set_current_module('FORTE') forte_options.get_options_from_psi4(psi4_options) # Setup forte and prepare the active space integral class nmopi = wfn.nmopi() point_group = wfn.molecule().point_group().symbol() mo_space_info = forte.make_mo_space_info(nmopi, point_group, forte_options) ints = forte.make_ints_from_psi4(wfn, forte_options, mo_space_info) as_ints = forte.make_active_space_ints(mo_space_info, ints, 'ACTIVE', ['RESTRICTED_DOCC']) print('\n\n => Sparse FCI Test <=') print(' Number of irreps: {}'.format(nirrep)) nmo = wfn.nmo() nmopi = [wfn.nmopi()[h] for h in range(nirrep)] nmopi_str = [str(wfn.nmopi()[h]) for h in range(nirrep)] mo_sym = [] for h in range(nirrep): for i in range(nmopi[h]): mo_sym.append(h) print(' Number of orbitals per irreps: [{}]'.format(','.join(nmopi_str))) print(' Symmetry of the MOs: ', mo_sym) hf_reference = forte.Determinant() hf_reference.create_alfa_bit(0) hf_reference.create_beta_bit(0) print(' Hartree-Fock determinant: {}'.format(hf_reference.str(10))) # Compute the HF energy hf_energy = as_ints.nuclear_repulsion_energy() + as_ints.slater_rules(hf_reference, hf_reference) print(' Nuclear repulsion energy: {}'.format(as_ints.nuclear_repulsion_energy())) print(' Reference energy: {}'.format(hf_energy)) # Build a list of determinants orblist = [i for i in range(nmo)] dets = [] for astr in itertools.combinations(orblist, na): for bstr in itertools.combinations(orblist, nb): sym = 0 d = forte.Determinant() for a in astr: d.create_alfa_bit(a) sym = sym ^ mo_sym[a] for b in bstr: d.create_beta_bit(b) sym = sym ^ mo_sym[b] if (sym == wfn_symmetry): dets.append(d) print(' Determinant {} has symmetry {}'.format(d.str(nmo), sym)) print(f'\n Size of the derminant basis: {len(dets)}') energy, evals, evecs, spin = forte.diag(dets, as_ints, 1, 1, "FULL") print(energy) efci = energy[0] + as_ints.nuclear_repulsion_energy() print('\n FCI Energy: {}\n'.format(efci)) assert efci == pytest.approx(ref_fci, abs=1e-9) # Clean up forte (necessary) forte.cleanup()
def prepare_forte_objects(wfn, mo_spaces=None, active_space='ACTIVE', core_spaces=['RESTRICTED_DOCC'], localize=False, localize_spaces=[]): """Take a psi4 wavefunction object and prepare the ForteIntegrals, SCFInfo, and MOSpaceInfo objects Parameters ---------- wfn : psi4 Wavefunction A psi4 Wavefunction object mo_spaces : dict A dictionary with the size of each space (e.g., {'ACTIVE' : [3]}) active_space : str The MO space treated as active (default: 'ACTIVE') core_spaces : list(str) The MO spaces treated as active (default: ['RESTRICTED_DOCC']) localize : bool Do localize the orbitals? (defaul: False) localize_spaces : list(str) A list of spaces to localize (default: []) Returns ------- tuple(ForteIntegrals, ActiveSpaceIntegrals, SCFInfo, MOSpaceInfo, map(StateInfo : list) a tuple containing the ForteIntegrals, SCFInfo, and MOSpaceInfo objects and a map of states and weights """ # fill in the options object psi4_options = psi4.core.get_options() psi4_options.set_current_module('FORTE') options = forte.forte_options options.get_options_from_psi4(psi4_options) if ('DF' in options.get_str('INT_TYPE')): aux_basis = psi4.core.BasisSet.build( wfn.molecule(), 'DF_BASIS_MP2', psi4.core.get_global_option('DF_BASIS_MP2'), 'RIFIT', psi4.core.get_global_option('BASIS')) wfn.set_basisset('DF_BASIS_MP2', aux_basis) if (options.get_str('MINAO_BASIS')): minao_basis = psi4.core.BasisSet.build( wfn.molecule(), 'MINAO_BASIS', psi4_options.get_str('MINAO_BASIS')) wfn.set_basisset('MINAO_BASIS', minao_basis) # Prepare base objects scf_info = forte.SCFInfo(wfn) nmopi = wfn.nmopi() point_group = wfn.molecule().point_group().symbol() if mo_spaces == None: mo_space_info = forte.make_mo_space_info(nmopi, point_group, options) else: mo_space_info = forte.make_mo_space_info_from_map( nmopi, point_group, mo_spaces, []) state_weights_map = forte.make_state_weights_map(options, mo_space_info) ints = forte.make_ints_from_psi4(wfn, options, mo_space_info) if localize: localizer = forte.Localize(forte.forte_options, ints, mo_space_info) localizer.set_orbital_space(localize_spaces) localizer.compute_transformation() Ua = localizer.get_Ua() ints.rotate_orbitals(Ua, Ua) # the space that defines the active orbitals. We select only the 'ACTIVE' part # the space(s) with non-active doubly occupied orbitals as_ints = forte.make_active_space_ints(mo_space_info, ints, active_space, core_spaces) return (ints, as_ints, scf_info, mo_space_info, state_weights_map)
def gradient_forte(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that forte can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> gradient('forte') available for : CASSCF """ # # Start Forte, initialize ambit # my_proc_n_nodes = forte.startup() # my_proc, n_nodes = my_proc_n_nodes # Get the psi4 option object optstash = p4util.OptionsState(['GLOBALS', 'DERTYPE']) psi4.core.set_global_option('DERTYPE', 'FIRST') # Build Forte options options = prepare_forte_options() # Print the banner forte.banner() # Run a method job_type = options.get_str('JOB_TYPE') if job_type not in {"CASSCF", "MCSCF_TWO_STEP"}: raise Exception( 'Analytic energy gradients are only implemented for job_types CASSCF and MCSCF_TWO_STEP.' ) # Prepare Forte objects: state_weights_map, mo_space_info, scf_info forte_objects = prepare_forte_objects(options, name, **kwargs) ref_wfn, state_weights_map, mo_space_info, scf_info, fcidump = forte_objects # Make an integral object time_pre_ints = time.time() ints = forte.make_ints_from_psi4(ref_wfn, options, mo_space_info) start = time.time() # Rotate orbitals before computation orb_type = options.get_str("ORBITAL_TYPE") if orb_type != 'CANONICAL': orb_t = forte.make_orbital_transformation(orb_type, scf_info, options, ints, mo_space_info) orb_t.compute_transformation() Ua = orb_t.get_Ua() Ub = orb_t.get_Ub() ints.rotate_orbitals(Ua, Ub) if job_type == "CASSCF": casscf = forte.make_casscf(state_weights_map, scf_info, options, mo_space_info, ints) energy = casscf.compute_energy() casscf.compute_gradient() if job_type == "MCSCF_TWO_STEP": casscf = forte.make_mcscf_two_step(state_weights_map, scf_info, options, mo_space_info, ints) energy = casscf.compute_energy() time_pre_deriv = time.time() derivobj = psi4.core.Deriv(ref_wfn) derivobj.set_deriv_density_backtransformed(True) derivobj.set_ignore_reference(True) grad = derivobj.compute(psi4.core.DerivCalcType.Correlated) ref_wfn.set_gradient(grad) optstash.restore() end = time.time() # Close ambit, etc. # forte.cleanup() # Print timings psi4.core.print_out('\n\n ==> Forte Timings <==\n') times = [('prepare integrals', start - time_pre_ints), ('run forte energy', time_pre_deriv - start), ('compute derivative integrals', end - time_pre_deriv)] max_key_size = max(len(k) for k, v in times) for key, value in times: psi4.core.print_out(f'\n Time to {key:{max_key_size}} :' f' {value:12.3f} seconds') psi4.core.print_out(f'\n {"Total":{max_key_size + 8}} :' f' {end - time_pre_ints:12.3f} seconds\n') # Dump orbitals if needed if options.get_bool('DUMP_ORBITALS'): dump_orbitals(ref_wfn) return ref_wfn
def test_sparse_ci(): import math import psi4 import forte import itertools import numpy as np import pytest from forte import forte_options ref_fci = -1.101150330132956 psi4.core.clean() # need to clean the options otherwise this job will interfere forte.clean_options() psi4.geometry(""" H H 1 1.0 """) psi4.set_options({'basis': 'sto-3g'}) E_scf, wfn = psi4.energy('scf', return_wfn=True) na = wfn.nalpha() nb = wfn.nbeta() nirrep = wfn.nirrep() wfn_symmetry = 0 psi4_options = psi4.core.get_options() psi4_options.set_current_module('FORTE') forte_options.get_options_from_psi4(psi4_options) # Setup forte and prepare the active space integral class nmopi = wfn.nmopi() point_group = wfn.molecule().point_group().symbol() mo_space_info = forte.make_mo_space_info(nmopi, point_group, forte_options) ints = forte.make_ints_from_psi4(wfn, forte_options, mo_space_info) as_ints = forte.make_active_space_ints(mo_space_info, ints, 'ACTIVE', ['RESTRICTED_DOCC']) as_ints.print() print('\n\n => Sparse FCI Test <=') print(' Number of irreps: {}'.format(nirrep)) nmo = wfn.nmo() nmopi = [wfn.nmopi()[h] for h in range(nirrep)] nmopi_str = [str(wfn.nmopi()[h]) for h in range(nirrep)] mo_sym = [] for h in range(nirrep): for i in range(nmopi[h]): mo_sym.append(h) print(' Number of orbitals per irreps: [{}]'.format(','.join(nmopi_str))) print(' Symmetry of the MOs: ', mo_sym) hf_reference = forte.Determinant() hf_reference.create_alfa_bit(0) hf_reference.create_beta_bit(0) print(' Hartree-Fock determinant: {}'.format(hf_reference.str(2))) # Compute the HF energy hf_energy = as_ints.nuclear_repulsion_energy() + as_ints.slater_rules(hf_reference, hf_reference) print(' Nuclear repulsion energy: {}'.format(as_ints.nuclear_repulsion_energy())) print(' Reference energy: {}'.format(hf_energy)) # Build a list of determinants orblist = [i for i in range(nmo)] dets = [] for astr in itertools.combinations(orblist, na): for bstr in itertools.combinations(orblist, nb): sym = 0 d = forte.Determinant() for a in astr: d.create_alfa_bit(a) sym = sym ^ mo_sym[a] for b in bstr: d.create_beta_bit(b) sym = sym ^ mo_sym[b] if (sym == wfn_symmetry): dets.append(d) print(' Determinant {} has symmetry {}'.format(d.str(nmo), sym)) # Build the Hamiltonian matrix using 'slater_rules' nfci = len(dets) H = np.ndarray((nfci, nfci)) for I in range(nfci): # off-diagonal terms for J in range(I + 1, nfci): HIJ = as_ints.slater_rules(dets[I], dets[J]) H[I][J] = H[J][I] = HIJ # diagonal term H[I][I] = as_ints.nuclear_repulsion_energy() + as_ints.slater_rules(dets[I], dets[I]) # Find the lowest eigenvalue efci = np.linalg.eigh(H)[0][0] print('\n FCI Energy: {}\n'.format(efci)) assert efci == pytest.approx(ref_fci, 1.0e-9)
def run_forte(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that forte can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('forte') """ # # Start Forte, initialize ambit # my_proc_n_nodes = forte.startup() # my_proc, n_nodes = my_proc_n_nodes # Build Forte options options = prepare_forte_options() # Print the banner forte.banner() # Prepare Forte objects: state_weights_map, mo_space_info, scf_info forte_objects = prepare_forte_objects(options, name, **kwargs) ref_wfn, state_weights_map, mo_space_info, scf_info, fcidump = forte_objects job_type = options.get_str('JOB_TYPE') if job_type == 'NONE' and options.get_str("ORBITAL_TYPE") == 'CANONICAL': psi4.core.set_scalar_variable('CURRENT ENERGY', 0.0) return ref_wfn start_pre_ints = time.time() if 'FCIDUMP' in options.get_str('INT_TYPE'): psi4.core.print_out('\n Forte will use custom integrals') # Make an integral object from the psi4 wavefunction object ints = make_ints_from_fcidump(fcidump, options, mo_space_info) else: psi4.core.print_out('\n Forte will use psi4 integrals') # Make an integral object from the psi4 wavefunction object ints = forte.make_ints_from_psi4(ref_wfn, options, mo_space_info) start = time.time() # Rotate orbitals before computation (e.g. localization, MP2 natural orbitals, etc.) orb_type = options.get_str("ORBITAL_TYPE") if orb_type != 'CANONICAL': orb_t = forte.make_orbital_transformation(orb_type, scf_info, options, ints, mo_space_info) orb_t.compute_transformation() Ua = orb_t.get_Ua() Ub = orb_t.get_Ub() ints.rotate_orbitals(Ua, Ub, job_type != 'NONE') # Run a method if job_type == 'NONE': psi4.core.set_scalar_variable('CURRENT ENERGY', 0.0) # forte.cleanup() return ref_wfn energy = 0.0 if (options.get_bool("CASSCF_REFERENCE") or job_type == "CASSCF"): if options.get_str('INT_TYPE') == 'FCIDUMP': raise Exception('Forte: the CASSCF code cannot use integrals read' ' from a FCIDUMP file') casscf = forte.make_casscf(state_weights_map, scf_info, options, mo_space_info, ints) energy = casscf.compute_energy() if (job_type == "MCSCF_TWO_STEP"): casscf = forte.make_mcscf_two_step(state_weights_map, scf_info, options, mo_space_info, ints) energy = casscf.compute_energy() if (job_type == 'NEWDRIVER'): energy = forte_driver(state_weights_map, scf_info, options, ints, mo_space_info) elif (job_type == 'MR-DSRG-PT2'): energy = mr_dsrg_pt2(job_type, forte_objects, ints, options) end = time.time() # Close ambit, etc. # forte.cleanup() psi4.core.set_scalar_variable('CURRENT ENERGY', energy) psi4.core.print_out( f'\n\n Time to prepare integrals: {start - start_pre_ints:12.3f} seconds' ) psi4.core.print_out( f'\n Time to run job : {end - start:12.3f} seconds') psi4.core.print_out( f'\n Total : {end - start_pre_ints:12.3f} seconds\n' ) if 'FCIDUMP' not in options.get_str('INT_TYPE'): if options.get_bool('DUMP_ORBITALS'): dump_orbitals(ref_wfn) return ref_wfn