how to pass HF result to SecondQuantizedMolecule? #372
-
Hi, |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 13 replies
-
Hello @jmaguiar, here is a script that should work to retrieve the psi4 wavefunction and parse it into a custom integral solver. You should replace the first call to In summary, we are defining a new This is not an ideal solution, and the code could be cleaned (there are redundant objects). A better option that we thought of in the past is to come up with an interface to The process would be more or less the same if you want to keep the call to Psi4 and change the parameter. You could copy/paste the code inside Script from tangelo import SecondQuantizedMolecule
import numpy as np
from tangelo.toolboxes.molecular_computation.integral_solver import IntegralSolver
from tangelo.toolboxes.molecular_computation.integral_solver_psi4 import IntegralSolverPsi4
class CustomIntegralSolverPsi4(IntegralSolver):
"""psi4 IntegralSolver class"""
def __init__(self, wfn):
import psi4
self.backend = psi4
self.backend.core.clean()
self.backend.core.clean_options()
self.backend.core.clean_variables()
self.wfn = wfn
def set_physical_data(self, mol):
self.mol = self.backend.geometry(mol.xyz)
mol.n_atoms = self.mol.natom()
mol.xyz = list()
for i in range(mol.n_atoms):
mol.xyz += [(self.mol.symbol(i), tuple(self.mol.xyz(i)[p]*0.52917721067 for p in range(3)))]
mol.n_electrons = self.wfn.nalpha() + self.wfn.nbeta()
mol.n_atoms = self.mol.natom()
def compute_mean_field(self, sqmol):
sqmol.mf_energy = self.wfn.energy()
sqmol.mo_energies = np.asarray(self.wfn.epsilon_a())
if sqmol.symmetry:
self.irreps = [self.mol.point_group().char_table().gamma(i).symbol().upper() for i in range(self.sym_wfn.nirrep())]
sym_mo_energies = []
tmp = self.backend.driver.p4util.numpy_helper._to_array(self.sym_wfn.epsilon_a(), dense=False)
for i in self.irreps:
sym_mo_energies += [(i, j, x) for j, x in enumerate(tmp[self.irreps.index(i)])]
ordered_energies = sorted(sym_mo_energies, key=lambda x: x[1])
sqmol.mo_symm_labels = [o[0] for o in ordered_energies]
sqmol.mo_symm_ids = [o[1] for o in ordered_energies]
else:
sqmol.mo_symm_labels = None
sqmol.mo_symm_ids = None
self.mints = self.backend.core.MintsHelper(self.wfn.basisset())
nbf = np.asarray(self.mints.ao_overlap()).shape[0]
if sqmol.uhf:
na = self.wfn.nalpha()
nb = self.wfn.nbeta()
sqmol.mo_occ = [[1]*na + (nbf-na)*[0]]+[[1]*nb + (nbf-nb)*[0]]
else:
docc = self.wfn.doccpi()[0]
socc = self.wfn.soccpi()[0]
sqmol.mo_occ = [2]*docc + [1]*socc + (nbf - docc - socc)*[0]
sqmol.n_mos = nbf
sqmol.n_sos = nbf*2
self.mo_coeff = np.asarray(self.wfn.Ca()) if not sqmol.uhf else np.array([np.asarray(self.wfn.Ca()), np.asarray(self.wfn.Cb())])
self.ob = np.asarray(self.mints.ao_potential()) + np.asarray(self.mints.ao_kinetic())
self.tb = np.asarray(self.mints.ao_eri())
self.core_constant = self.mol.nuclear_repulsion_energy()
def modify_solver_mo_coeff(self, sqmol):
if not sqmol.uhf:
self.modify_c(self.wfn, self.mo_coeff)
else:
self.modify_c(self.wfn, self.mo_coeff[0])
self.modify_c(self.wfn, self.mo_coeff[1], False)
def modify_c(self, wfn, mo_coeff, a=True):
for i in range(mo_coeff.shape[0]):
for j in range(mo_coeff.shape[1]):
if a:
wfn.Ca().set(0, i, j, mo_coeff[i, j])
else:
wfn.Cb().set(0, i, j, mo_coeff[i, j])
def get_integrals(self, sqmol, mo_coeff=None):
if mo_coeff is None:
mo_coeff = self.mo_coeff
if sqmol.uhf:
return self.compute_uhf_integrals(mo_coeff)
ob = mo_coeff.T@self.ob@mo_coeff
eed = self.tb.copy()
eed = np.einsum("ij,jlmn -> ilmn", mo_coeff.T, eed)
eed = np.einsum("kl,jlmn -> jkmn", mo_coeff.T, eed)
eed = np.einsum("jlmn, mk -> jlkn", eed, mo_coeff)
eed = np.einsum("jlmn, nk -> jlmk", eed, mo_coeff)
tb = eed.transpose(0, 2, 3, 1)
return self.core_constant, ob, tb
def compute_uhf_integrals(self, mo_coeff):
mo_a = self.backend.core.Matrix.from_array(mo_coeff[0])
mo_b = self.backend.core.Matrix.from_array(mo_coeff[1])
# calculate alpha and beta one-body integrals
hpq = [mo_coeff[0].T.dot(self.ob).dot(mo_coeff[0]), mo_coeff[1].T.dot(self.ob).dot(mo_coeff[1])]
# mo transform the two-electron integrals
eri_a = np.asarray(self.mints.mo_eri(mo_a, mo_a, mo_a, mo_a))
eri_b = np.asarray(self.mints.mo_eri(mo_b, mo_b, mo_b, mo_b))
eri_ba = np.asarray(self.mints.mo_eri(mo_a, mo_a, mo_b, mo_b))
# # convert this into physicist ordering for OpenFermion
two_body_integrals_a = np.asarray(eri_a.transpose(0, 2, 3, 1), order='C')
two_body_integrals_b = np.asarray(eri_b.transpose(0, 2, 3, 1), order='C')
two_body_integrals_ab = np.asarray(eri_ba.transpose(0, 2, 3, 1), order='C')
# Gpqrs has alpha, alphaBeta, Beta blocks
Gpqrs = (two_body_integrals_a, two_body_integrals_ab, two_body_integrals_b)
return self.core_constant, hpq, Gpqrs
if __name__ == "__main__":
mol = SecondQuantizedMolecule("H 0. 0. 0.\nH 0. 0. 0.75", solver=IntegralSolverPsi4())
print(mol)
wfn = mol.solver.wfn
mol = SecondQuantizedMolecule("H 0. 0. 0.\nH 0. 0. 0.75", solver=CustomIntegralSolverPsi4(wfn))
print(mol) |
Beta Was this translation helpful? Give feedback.
Hello @jmaguiar, here is a script that should work to retrieve the psi4 wavefunction and parse it into a custom integral solver. You should replace the first call to
SecondQuantizedMolecule
with your code to get the wavefunction. This was just to get a wavefunction object on my end.In summary, we are defining a new
IntegralSolver
class that takes an existing psi4 wavefunction, and get the required molecular orbital coefficients and integrals from the wavefunction object.This is not an ideal solution, and the code could be cleaned (there are redundant objects). A better option that we thought of in the past is to come up with an interface to
FCIDUMP
files, as psi4 can output integrals to…