Skip to content

Commit

Permalink
Merge 7a0e0e7 into fe3d42c
Browse files Browse the repository at this point in the history
  • Loading branch information
jonasleitner committed Jan 20, 2022
2 parents fe3d42c + 7a0e0e7 commit 7587d81
Show file tree
Hide file tree
Showing 10 changed files with 544 additions and 4 deletions.
16 changes: 15 additions & 1 deletion adcc/OperatorIntegrals.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ def available(self):
"electric_dipole",
"magnetic_dipole",
"nabla",
"pe_induction_elec"
"pe_induction_elec",
"pcm_potential_elec"
)
return [integral for integral in integrals
if hasattr(self.provider_ao, integral)]
Expand Down Expand Up @@ -187,6 +188,19 @@ def pe_induction_elec(self):
callback = self.provider_ao.pe_induction_elec
return self.__import_density_dependent_operator(callback)

@property
def pcm_potential_elec(self):
"""
Returns a function to obtain the (density-dependent)
electronic PCM potential operator in the molecular orbital basis
"""
if "pcm_potential_elec" not in self.available:
raise NotImplementedError("electronic PCM potential operator "
"not implemented "
f"in {self.provider_ao.backend} backend.")
callback = self.provider_ao.pcm_potential_elec
return self.__import_density_dependent_operator(callback)

@property
def timer(self):
ret = Timer()
Expand Down
19 changes: 19 additions & 0 deletions adcc/adc_pp/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,22 @@ def apply(ampl):
vpe = op.pe_induction_elec(tdm)
return AmplitudeVector(ph=vpe.ov)
return AdcBlock(apply, 0)


def block_ph_ph_0_pcm(hf, mp, intermediates):
"""
Constructs an :py:class:`AdcBlock` that describes the
linear response coupling to the polarizable environment
from PCM via a CIS-like transition density as described
in 10.1021/ct300763v, eq 63. Since the contribution
depends on the input amplitude itself,
a diagonal term cannot be formulated.
"""
op = hf.operators

def apply(ampl):
tdm = OneParticleOperator(mp, is_symmetric=False)
tdm.vo = ampl.ph.transpose()
vpcm = op.pcm_potential_elec(tdm)
return AmplitudeVector(ph=vpcm.ov)
return AdcBlock(apply, 0)
27 changes: 27 additions & 0 deletions adcc/backends/psi4.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ def pe_induction_elec_ao(dm):
)[1]
return pe_induction_elec_ao

@property
def pcm_potential_elec(self):
if self.wfn.PCM_enabled():
def pcm_potential_elec_ao(dm):
return psi4.core.PCM.compute_V(
self.wfn.get_PCM(),
psi4.core.Matrix.from_array(dm.to_ndarray())
)
return pcm_potential_elec_ao


class Psi4EriBuilder(EriBuilder):
def __init__(self, wfn, n_orbs, n_orbs_alpha, n_alpha, n_beta, restricted):
Expand Down Expand Up @@ -106,6 +116,13 @@ def pe_energy(self, dm, elec_only=True):
elec_only=elec_only)
return e_pe

def pcm_energy(self, dm):
psi_dm = psi4.core.Matrix.from_array(dm.to_ndarray())
# computes the Fock matrix contribution
# By contraction with the tdm, the electronic contribution is obtained
V_pcm = psi4.core.PCM.compute_V(self.wfn.get_PCM(), psi_dm).to_array()
return np.einsum("uv,uv->", dm.to_ndarray(), V_pcm)

@property
def excitation_energy_corrections(self):
ret = []
Expand All @@ -121,13 +138,21 @@ def excitation_energy_corrections(self):
elec_only=True)
)
ret.extend([ptlr, ptss])
if self.environment == "pcm":
ptlr = EnergyCorrection(
"pcm_ptlr_correction",
lambda view: self.pcm_energy(view.transition_dm_ao)
)
ret.extend([ptlr])
return {ec.name: ec for ec in ret}

@property
def environment(self):
ret = None
if hasattr(self.wfn, "pe_state"):
ret = "pe"
elif self.wfn.PCM_enabled():
ret = "pcm"
return ret

def get_backend(self):
Expand Down Expand Up @@ -245,6 +270,8 @@ def run_hf(xyz, basis, charge=0, multiplicity=1, conv_tol=1e-11,
"ccpvdz": "cc-pvdz",
}

# needed for PE and PCM tests
psi4.core.clean_options()
mol = psi4.geometry(f"""
{charge} {multiplicity}
{xyz}
Expand Down
39 changes: 36 additions & 3 deletions adcc/backends/pyscf.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
from ..exceptions import InvalidReference
from ..ExcitedStates import EnergyCorrection

from pyscf import ao2mo, gto, scf, solvent
from pyscf import ao2mo, gto, scf
from pyscf.solvent import pol_embed, ddcosmo


class PyScfOperatorIntegralProvider:
Expand Down Expand Up @@ -59,13 +60,28 @@ def nabla(self):
@property
def pe_induction_elec(self):
if hasattr(self.scfres, "with_solvent"):
if isinstance(self.scfres.with_solvent, solvent.pol_embed.PolEmbed):
if isinstance(self.scfres.with_solvent, pol_embed.PolEmbed):
def pe_induction_elec_ao(dm):
return self.scfres.with_solvent._exec_cppe(
dm.to_ndarray(), elec_only=True
)[1]
return pe_induction_elec_ao

@property
def pcm_potential_elec(self):
if hasattr(self.scfres, "with_solvent"):
if isinstance(self.scfres.with_solvent, ddcosmo.DDCOSMO):
def pcm_potential_elec_ao(dm):
# Since eps (dielectric constant) is the only solvent parameter
# in pyscf and there is no solvent data available in the
# program, the user needs to adjust scfres.with_solvent.eps
# manually to the optical dielectric constant (if non
# equilibrium solvation is desired).
return self.scfres.with_solvent._B_dot_x(
dm.to_ndarray()
)
return pcm_potential_elec_ao


# TODO: refactor ERI builder to be more general
# IntegralBuilder would be good
Expand Down Expand Up @@ -125,6 +141,15 @@ def pe_energy(self, dm, elec_only=True):
e_pe, _ = pe_state.kernel(dm.to_ndarray(), elec_only=elec_only)
return e_pe

def pcm_energy(self, dm):
# Since eps (dielectric constant) is the only solvent parameter
# in pyscf and there is no solvent data available in the
# program, the user needs to adjust scfres.with_solvent.eps
# manually to the optical dielectric constant (if non
# equilibrium solvation is desired).
V_pcm = self.scfres.with_solvent._B_dot_x(dm.to_ndarray())
return np.einsum("uv,uv->", dm.to_ndarray(), V_pcm)

@property
def excitation_energy_corrections(self):
ret = []
Expand All @@ -140,14 +165,22 @@ def excitation_energy_corrections(self):
elec_only=True)
)
ret.extend([ptlr, ptss])
elif self.environment == "pcm":
ptlr = EnergyCorrection(
"pcm_ptlr_correction",
lambda view: self.pcm_energy(view.transition_dm_ao)
)
ret.extend([ptlr])
return {ec.name: ec for ec in ret}

@property
def environment(self):
ret = None
if hasattr(self.scfres, "with_solvent"):
if isinstance(self.scfres.with_solvent, solvent.pol_embed.PolEmbed):
if isinstance(self.scfres.with_solvent, pol_embed.PolEmbed):
ret = "pe"
elif isinstance(self.scfres.with_solvent, ddcosmo.DDCOSMO):
ret = "pcm"
return ret

def get_backend(self):
Expand Down

0 comments on commit 7587d81

Please sign in to comment.