Skip to content

Commit

Permalink
Merge pull request #131 from BoothGroup/ewdmet_scmf
Browse files Browse the repository at this point in the history
Implements quasiparticle EWDMET to obtain spectral functions from embedded calculations, making use of the Dyson package for moment based approaches to Green's function calculations.
  • Loading branch information
basilib authored Mar 27, 2024
2 parents 79a4678 + 9bc14e2 commit 2ff12e4
Show file tree
Hide file tree
Showing 20 changed files with 1,539 additions and 151 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ pyscf.out
# Vayesta log files
vlog.txt
verr.txt
f*_pyscf.txt
f*.out
*.h5

# HDF5 files
*.h5
Expand Down
67 changes: 67 additions & 0 deletions examples/ewdmet/01-sc-qp-ewdmet-hubbard1d-exact-limit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import numpy as np

import vayesta
import vayesta.ewf
from vayesta.lattmod import Hubbard1D, LatticeRHF

from dyson import MBLGF, AuxiliaryShift, FCI, MixedMBLGF, NullLogger, Lehmann

nsite = 10
nelec = nsite
u = 6
nfrag = 2

nmom_max_fci = (4,4)
nmom_max_bath=1




hubbard = Hubbard1D(nsite=nsite, nelectron=nelec, hubbard_u=u, verbose=0)
mf = LatticeRHF(hubbard)
mf.kernel()

# Full system FCI GF
expr = FCI["1h"](mf)
th = expr.build_gf_moments(nmom_max_fci[0])
expr = FCI["1p"](mf)
tp = expr.build_gf_moments(nmom_max_fci[1])

solverh = MBLGF(th, log=NullLogger())
solverp = MBLGF(tp, log=NullLogger())
solver = MixedMBLGF(solverh, solverp)
solver.kernel()
se = solver.get_self_energy()
solver = AuxiliaryShift(th[1]+tp[1], se, nelec, log=NullLogger())
solver.kernel()
static_potential = se.as_static_potential(mf.mo_energy, eta=1e-2)
gf = solver.get_greens_function()
dm = gf.occupied().moment(0) * 2.0
nelec_gf = np.trace(dm)
print("Exact GF nelec: %s"%nelec_gf)

sc = mf.get_ovlp() @ mf.mo_coeff
new_fock = sc @ (th[1] + tp[1] + static_potential) @ sc.T
e, mo_coeff = np.linalg.eigh(new_fock)
chempot = (e[nelec//2-1] + e[nelec//2] ) / 2
gf_static = Lehmann(e, mo_coeff, chempot=chempot)

gap = lambda gf: gf.physical().virtual().energies[0] - gf.physical().occupied().energies[-1]
dynamic_gap = gap(gf)
static_gap = gap(gf_static)

# QP-EwDMET GF
opts = dict(sc=False, store_hist=True, aux_shift=True, store_scfs=True, diis=True, damping=0, static_potential_conv_tol=1e-6, use_sym=False, eta=1e-2)
emb = vayesta.ewf.EWF(mf, solver='FCI', bath_options=dict(bathtype='full', dmet_threshold=1e-12), solver_options=dict(conv_tol=1e-15, n_moments=nmom_max_fci))
emb.qpewdmet_scmf(proj=1, maxiter=10, **opts)
nimages = [nsite//nfrag, 1, 1]
emb.symmetry.set_translations(nimages)
with emb.site_fragmentation() as f:
f.add_atomic_fragment(range(nfrag))
emb.kernel()

emb_dynamic_gap = gap(emb.with_scmf.gf)
emb_static_gap = gap(emb.with_scmf.gf_qp)
print("Ran for %s iterations"%emb.with_scmf.iteration)
print("Dynamic gap, FCI: %s Emb: %s"%(dynamic_gap, emb_dynamic_gap))
print("Static gap, FCI: %s Emb: %s"%(static_gap, emb_static_gap))
65 changes: 65 additions & 0 deletions examples/ewdmet/02-sc-qp-ewdmet-hubbard1d-projectors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import numpy as np

import vayesta
import vayesta.ewf
from vayesta.lattmod import Hubbard1D, LatticeRHF

# Plot the spectrum
from dyson.util import build_spectral_function
import matplotlib.pyplot as plt

nsite = 12
nelec = nsite
nfrag = 2
u = 3

nmom_max_fci = (4,4)
nmom_max_bath=1


hubbard = Hubbard1D(nsite=nsite, nelectron=nelec, hubbard_u=u, verbose=0)
mf = LatticeRHF(hubbard)
mf.kernel()

emb = vayesta.ewf.EWF(mf, solver='FCI', bath_options=dict(bathtype='ewdmet', max_order=1, order=1, dmet_threshold=1e-10), solver_options=dict(conv_tol=1e-12, init_guess='cisd', n_moments=nmom_max_fci))
nimages = [nsite//nfrag, 1, 1]
emb.symmetry.set_translations(nimages)
with emb.site_fragmentation() as f:
f.add_atomic_fragment(list(range(nfrag)))
emb.qpewdmet_scmf(maxiter=100, proj=1)
emb.kernel()
gf, gf_qp = emb.with_scmf.get_greens_function()

fig, ax = plt.subplots(2,1, figsize=(16,9))

grid = np.linspace(-5, 11, 1024)
sf_hf = build_spectral_function(mf.mo_energy, np.eye(mf.mo_occ.size), grid, eta=0.1)
ax[0].plot(grid, sf_hf, 'r-', label='HF')
ax[1].plot(grid, sf_hf, 'r-', label='HF')

sf_dynamic = build_spectral_function(gf.energies, gf.couplings, grid, eta=0.1)
sf_static = build_spectral_function(gf_qp.energies, gf_qp.couplings, grid, eta=0.1)
ax[0].plot(grid, sf_dynamic, "b-", label="QP-EwDMET (1 Proj, dynamic)")
ax[1].plot(grid, sf_static, "g-", label="QP-EwDMET (1 Proj, static)")


emb = vayesta.ewf.EWF(mf, solver='FCI', bath_options=dict(bathtype='ewdmet', max_order=1, order=1, dmet_threshold=1e-10), solver_options=dict(conv_tol=1e-12, init_guess='cisd', n_moments=nmom_max_fci))
nimages = [nsite//nfrag, 1, 1]
emb.symmetry.set_translations(nimages)
with emb.site_fragmentation() as f:
f.add_atomic_fragment(list(range(nfrag)))
emb.qpewdmet_scmf(maxiter=100, proj=2)
emb.kernel()
gf, gf_qp = emb.with_scmf.get_greens_function()

sf_dynamic = build_spectral_function(gf.energies, gf.couplings, grid, eta=0.1)
sf_static = build_spectral_function(gf_qp.energies, gf_qp.couplings, grid, eta=0.1)
ax[0].plot(grid, sf_dynamic, "m-", label="QP-EwDMET (2 Proj, dynamic)")
ax[1].plot(grid, sf_static, "y-", label="QP-EwDMET (2, Proj, static)")


ax[0].set_title('U = %d'%u)
ax[0].legend()
ax[1].legend()

plt.savefig("hubbard_spectral_function.png")
42 changes: 42 additions & 0 deletions examples/ewdmet/03-sc-qp-ewdmet-hubbard1d-bethe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import numpy as np
import vayesta
import vayesta.ewf
from vayesta.lattmod import Hubbard1D, LatticeRHF
from vayesta.lattmod.bethe import hubbard1d_bethe_gap
from dyson import Lehmann, FCI, CCSD

u = 3
nsite = 128
nelec = nsite
nfrag = 2
nmom_max_bath = 1
nmom_max_fci = (4,4)
solv = 'FCI'
EXPR = FCI if solv=='FCI' else CCSD
hubbard = Hubbard1D(nsite=nsite, nelectron=nelec, hubbard_u=u, verbose=0)
mf = LatticeRHF(hubbard)
mf.kernel()

chempot = (mf.mo_energy[nsite//2-1] + mf.mo_energy[nsite//2] ) / 2
gf_hf = Lehmann(mf.mo_energy, np.eye(mf.mo_coeff.shape[0]), chempot=chempot)


emb = vayesta.ewf.EWF(mf, solver=solv, bath_options=dict(bathtype='ewdmet', max_order=nmom_max_bath, order=nmom_max_bath, dmet_threshold=1e-12), solver_options=dict(conv_tol=1e-12, n_moments=nmom_max_fci))
emb.qpewdmet_scmf(proj=2, maxiter=10)
nimages = [nsite//nfrag, 1, 1]
emb.symmetry.set_translations(nimages)
with emb.site_fragmentation() as f:
f.add_atomic_fragment(list(range(nfrag)))
emb.kernel()

gap = lambda gf: gf.physical().virtual().energies[0] - gf.physical().occupied().energies[-1]
emb_dynamic_gap = gap(emb.with_scmf.gf)
emb_static_gap = gap(emb.with_scmf.gf_qp)
hf_gap = gap(gf_hf)
bethe_gap = hubbard1d_bethe_gap(1,u, interval=(1,200))

print("Ran for %s iterations"%emb.with_scmf.iteration)
print("Bethe ansatz gap: %s "%(bethe_gap))
print("Hartree-Fock gap: %s"%(hf_gap))
print("Dynamic GF gap: %s"%(emb_dynamic_gap))
print("Static GF gap: %s"%(emb_static_gap))
77 changes: 77 additions & 0 deletions examples/ewdmet/10-sc-qp-ewdmet-hydrogen-ring-exact-limit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import numpy as np
import scipy

import pyscf
import pyscf.scf

import vayesta
import vayesta.ewf
from vayesta.misc.molecules import ring
from dyson import MBLGF, AuxiliaryShift, FCI, MixedMBLGF, NullLogger, Lehmann
from dyson.util import build_spectral_function

a = 1
natom = 10
nfrag = 1
maxiter = 100
nmom_max_fci = (4,4)
nmom_max_bath=1

mol = pyscf.gto.Mole()
mol.atom = ring('H', natom, a)
mol.basis = 'sto-3g'
mol.output = 'pyscf.out'
mol.verbose = 4
mol.build()

# Hartree-Fock
mf = pyscf.scf.RHF(mol)
mf.kernel()
assert mf.converged
chempot = (mf.mo_energy[natom//2-1] + mf.mo_energy[natom//2] ) / 2
gf_hf = Lehmann(mf.mo_energy, np.eye(mf.mo_coeff.shape[0]), chempot=chempot)

# Full system FCI GF
expr = FCI["1h"](mf)
th = expr.build_gf_moments(nmom_max_fci[0])
expr = FCI["1p"](mf)
tp = expr.build_gf_moments(nmom_max_fci[1])

solverh = MBLGF(th, log=NullLogger())
solverp = MBLGF(tp, log=NullLogger())
solver = MixedMBLGF(solverh, solverp)
solver.kernel()
se = solver.get_self_energy()
solver = AuxiliaryShift(th[1]+tp[1], se, natom, log=NullLogger())
solver.kernel()
static_potential = se.as_static_potential(mf.mo_energy, eta=1e-2)
gf = solver.get_greens_function()
dm = gf.occupied().moment(0) * 2.0
nelec_gf = np.trace(dm)
print("Exact GF nelec: %s"%nelec_gf)

sc = mf.get_ovlp() @ mf.mo_coeff
new_fock = sc @ (th[1] + tp[1] + static_potential) @ sc.T
e, mo_coeff = scipy.linalg.eigh(new_fock, mf.get_ovlp())
chempot = (e[natom//2-1] + e[natom//2] ) / 2
gf_static = Lehmann(e, np.eye(mf.mo_coeff.shape[0]), chempot=chempot)

gap = lambda gf: gf.physical().virtual().energies[0] - gf.physical().occupied().energies[-1]
dynamic_gap = gap(gf)
static_gap = gap(gf_static)

# QP-EwDMET GF
emb = vayesta.ewf.EWF(mf, solver='FCI', bath_options=dict(bathtype='full', dmet_threshold=1e-12), solver_options=dict(conv_tol=1e-15, n_moments=nmom_max_fci))
emb.qpewdmet_scmf(proj=1, maxiter=maxiter)
with emb.site_fragmentation() as f:
with f.rotational_symmetry(order=int(natom/nfrag), axis='z') as rot:
f.add_atomic_fragment(range(nfrag))
emb.kernel()

hf_gap = gap(gf_hf)
emb_dynamic_gap = gap(emb.with_scmf.gf)
emb_static_gap = gap(emb.with_scmf.gf_qp)
print("Ran for %s iterations"%emb.with_scmf.iteration)
print("Hartree-Fock gap: %s"%hf_gap)
print("Dynamic gap, FCI: %s Emb: %s"%(dynamic_gap, emb_dynamic_gap))
print("Static gap, FCI: %s Emb: %s"%(static_gap, emb_static_gap))
6 changes: 3 additions & 3 deletions examples/ewf/molecules/42-fci-incluster-moments.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@
H 0.0000 0.7572 -0.4692
H 0.0000 -0.7572 -0.4692
"""
mol.basis = "cc-pVDZ"
mol.basis = "sto-6g"
mol.output = "pyscf.txt"
mol.build()

# Hartree-Fock
mf = pyscf.scf.RHF(mol)
mf.kernel()

# Embedded CCSD
emb = vayesta.ewf.EWF(mf, bath_options=dict(threshold=1e-6), solver_options=dict(n_moments=(5, 4), solve_lambda=True))
# Embedded FCI
emb = vayesta.ewf.EWF(mf, solver='FCI', bath_options=dict(threshold=1e-6), solver_options=dict(n_moments=(5, 4)))
emb.kernel()

# Reference full system CCSD:
Expand Down
10 changes: 9 additions & 1 deletion vayesta/core/qemb/qemb.py
Original file line number Diff line number Diff line change
Expand Up @@ -1732,7 +1732,15 @@ def brueckner_scmf(self, *args, **kwargs):
"""Decorator for Brueckner-DMET."""
self.with_scmf = Brueckner(self, *args, **kwargs)
self.kernel = self.with_scmf.kernel

def qpewdmet_scmf(self, *args, **kwargs):
"""Decorator for QP-EWDMET."""
try:
from vayesta.core.scmf import QPEWDMET
except ImportError:
self.log.error("QP-EWDMET requires Dyson installed")
return
self.with_scmf = QPEWDMET(self, *args, **kwargs)
self.kernel = self.with_scmf.kernel
def check_solver(self, solver):
is_uhf = np.ndim(self.mo_coeff[1]) == 2
if self.opts.screening:
Expand Down
Loading

0 comments on commit 2ff12e4

Please sign in to comment.