from vayesta.core.bath.bath import Bath
from vayesta.core.bosonic_bath.bbath import Bosonic_Bath
from vayesta.core.util import dot, einsum
import numpy as np
[docs]class RPA_Boson_Target_Space(Bath):
"""Class to obtain the target excitation space from which we'll construct our bosonic bath.
This can either start from either the DMET or fully extended cluster, and can be optionally projected onto
excitations local to the fragment in either just the occupied space or both the occupied and virtual spaces.
"""
def __init__(self, fragment, target_orbitals="full", local_projection="fragment"):
self.target_orbitals = target_orbitals
self.local_projection = local_projection
super().__init__(fragment)
if not self.spin_restricted:
raise NotImplementedError("Spin-unrestricted RPA bosonic bath not yet implemented.")
@property
def mo_coeff_occ(self):
return self.fragment.base.mo_coeff_occ
@property
def mo_coeff_vir(self):
return self.fragment.base.mo_coeff_vir
@property
def ovlp(self):
return self.fragment.base.get_ovlp()
[docs] def get_c_target(self):
if self.target_orbitals == "full":
return dot(self.mo_coeff_occ.T, self.ovlp, self.fragment.cluster.c_active_occ), dot(
self.mo_coeff_vir.T, self.ovlp, self.fragment.cluster.c_active_vir
)
elif self.target_orbitals == "dmet":
return dot(self.mo_coeff_occ.T, self.ovlp, self.fragment._dmet_bath.c_cluster_occ), dot(
self.mo_coeff_vir.T, self.ovlp, self.fragment._dmet_bath.c_cluster_vir
)
else:
raise ValueError("Unknown target orbital requested.")
[docs] def get_c_loc(self):
if self.local_projection is None:
return None, None
if "fragment" in self.local_projection:
if len(self.local_projection) == 8 or self.local_projection[-3:] == "occ":
return dot(self.mo_coeff_occ.T, self.ovlp, self.fragment.c_frag), None
elif self.local_projection[-2:] == "ov":
return dot(self.mo_coeff_occ.T, self.ovlp, self.fragment.c_frag), dot(
self.mo_coeff_vir.T, self.ovlp, self.fragment.c_frag
)
raise ValueError("Unknown fragment projection requested.")
[docs] def gen_target_excitation(self):
"""Generate the targeted excitation space for a given fragment"""
self.fragment.log.debug(
"Fragment %s generating RPA bosonic bath using %s excitations and projection onto %s.",
self.fragment.id,
self.target_orbitals,
self.local_projection,
)
# Obtain all values in the equivalent global space.
c_occ, c_vir = self.get_c_target()
c_loc_occ, c_loc_vir = self.get_c_loc()
if c_loc_occ is not None:
s_occ = dot(c_loc_occ.T, c_occ)
c_occ = dot(c_occ, s_occ.T, s_occ)
if c_loc_vir is not None:
s_vir = dot(c_loc_vir.T, c_vir)
c_vir = dot(c_vir, s_vir.T, s_vir)
tar_ss = einsum("iI,aA->IAia", c_occ, c_vir).reshape(-1, c_occ.shape[0] * c_vir.shape[0])
return np.hstack((tar_ss, tar_ss))
def _get_target_orbitals(self):
if self.target_orbitals == "full":
return self.fragment.c_occ, self.fragment.c_vir
elif self.target_orbitals == "dmet":
dmet_bath = self.fragment._dmet_bath
return dmet_bath.c_cluster_occ, dmet_bath.c_cluster_vir
raise ValueError("Unknown target orbital requested.")
[docs]class RPA_QBA_Bath(Bosonic_Bath):
def __init__(self, fragment, target_m0):
self.target_m0 = target_m0
super().__init__(fragment)
[docs] def make_boson_coeff(self):
# Generate full local fermionic excitation space.
clus_ov = self.cluster_excitations
# Remove any contributions within the fermionic excitation space of the fragment.
m0_env = self.target_m0 - dot(dot(self.target_m0, clus_ov.T), clus_ov)
# Now we can construct the bosonic bath by diagonalising these contributions.
contribs = dot(m0_env, m0_env.T)
sort = np.s_[::-1]
occup, c = np.linalg.eigh(contribs)
occup = occup[sort]
c = c[:, sort]
occuprtinv = occup ** (-0.5)
coeff = dot((c * occuprtinv[None]).T, m0_env)
self.coeff = coeff
self.occup = occup
return coeff, occup