Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
NVIDIA
GitHub Repository: NVIDIA/cuda-q-academic
Path: blob/main/chemistry-simulations/aux_files/qmmm/qchem/cppe_lib.py
1165 views
1
try:
2
import numpy as np
3
except ValueError:
4
print('Numpy should be installed')
5
6
#`try:`
7
#` from pyscf import gto,scf,solvent`
8
#`except ValueError:`
9
#` print('PySCF should be installed. Use pip install pyscf')`
10
11
try:
12
import cppe
13
except ValueError:
14
print(
15
'cppe library should be installed to use the polaizable embedding model. Use pip install cppe'
16
)
17
18
#` The CPPE library can be installed via: pip install cppe`
19
#` You might need to use (apt-get update & apt-get install build-essential -y`
20
#` apt-get install python3-dev) before installing cppe.`
21
####################################
22
23
24
class PolEmbed:
25
26
def __init__(self, mol, peoptions):
27
28
self.mol = mol
29
30
if isinstance(peoptions, str):
31
options = {"potfile": peoptions}
32
else:
33
options = peoptions
34
35
if not isinstance(options, dict):
36
raise TypeError("Options should be a dictionary.")
37
38
self.options = options
39
self.cppe_state = self._cppe_state(mol)
40
self.potentials = self.cppe_state.potentials
41
self.polarizable_coords = np.array([
42
site.position
43
for site in self.cppe_state.potentials
44
if site.is_polarizable
45
])
46
47
self.V_es = None
48
49
def _cppe_state(self, mol):
50
51
cppe_mol = cppe.Molecule()
52
for z, coord in zip(mol.atom_charges(), mol.atom_coords()):
53
cppe_mol.append(cppe.Atom(z, *coord))
54
55
cppe_state = cppe.CppeState(self.options, cppe_mol)
56
cppe_state.calculate_static_energies_and_fields()
57
58
return cppe_state
59
60
def _compute_field_integrals(self, site, moment):
61
self.mol.set_rinv_orig(site)
62
integral = self.mol.intor("int1e_iprinv") + self.mol.intor(
63
"int1e_iprinv").transpose(0, 2, 1)
64
op = np.einsum('aij,a->ij', integral, -1.0 * moment)
65
66
return op
67
68
def _compute_field(self, site, dm):
69
70
self.mol.set_rinv_orig(site)
71
integral = self.mol.intor("int1e_iprinv") + self.mol.intor(
72
"int1e_iprinv").transpose(0, 2, 1)
73
74
return np.einsum('ij,aij->a', dm, integral)
75
76
def _compute_multipole_potential_integrals(self, site, order, moments):
77
78
if order > 2:
79
raise NotImplementedError(
80
"""Multipole potential integrals not implemented for order > 2."""
81
)
82
83
self.mol.set_rinv_orig(site)
84
85
# Order 0:
86
#if order==0:
87
integral0 = self.mol.intor("int1e_rinv")
88
es_operator = integral0 * moments[0] * cppe.prefactors(0)
89
90
# Order 1:
91
if order > 0:
92
integral1 = self.mol.intor("int1e_iprinv") + self.mol.intor(
93
"int1e_iprinv").transpose(0, 2, 1)
94
es_operator += np.einsum('aij,a->ij', integral1,
95
moments[1] * cppe.prefactors(1))
96
97
# Order 2:
98
if order > 1:
99
integral2 = self.mol.intor("int1e_ipiprinv") + self.mol.intor("int1e_ipiprinv").transpose(0, 2, 1) \
100
+ 2.0 * self.mol.intor("int1e_iprinvip")
101
# add the lower triangle to the upper triangle
102
integral2[1] += integral2[3]
103
integral2[2] += integral2[6]
104
integral2[5] += integral2[7]
105
integral2[1] *= 0.5
106
integral2[2] *= 0.5
107
integral2[5] *= 0.5
108
109
es_operator += np.einsum('aij,a->ij',
110
integral2[[0, 1, 2, 4, 5, 8], :, :],
111
moments[2] * cppe.prefactors(2))
112
113
return es_operator
114
115
def get_pe_contribution(self, dm, elec_only=False):
116
117
# Step I: Build electrostatic operator
118
if self.V_es is None:
119
self.V_es = np.zeros((self.mol.nao, self.mol.nao), dtype=np.float64)
120
for p in self.potentials:
121
moments = []
122
for m in p.multipoles:
123
m.remove_trace()
124
moments.append(m.values)
125
self.V_es += self._compute_multipole_potential_integrals(
126
p.position, m.k, moments)
127
128
e_static = np.einsum('ij,ij->', self.V_es, dm)
129
self.cppe_state.energies["Electrostatic"]["Electronic"] = e_static
130
131
#` Step II: obtain expectation values of elec. field at polarizable sites`
132
n_sitecoords = 3 * self.cppe_state.get_polarizable_site_number()
133
V_ind = np.zeros((self.mol.nao, self.mol.nao), dtype=np.float64)
134
if n_sitecoords:
135
current_polsite = 0
136
elec_fields = np.zeros(n_sitecoords, dtype=np.float64)
137
for p in self.potentials:
138
if not p.is_polarizable:
139
continue
140
elec_fields_s = self._compute_field(p.position, dm)
141
elec_fields[3 * current_polsite:3 * current_polsite +
142
3] = elec_fields_s
143
current_polsite += 1
144
145
# Step III: solve induced moments
146
self.cppe_state.update_induced_moments(elec_fields, elec_only)
147
induced_moments = np.array(self.cppe_state.get_induced_moments())
148
149
# Step IV: build induction operator
150
current_polsite = 0
151
for p in self.potentials:
152
if not p.is_polarizable:
153
continue
154
site = p.position
155
V_ind += self._compute_field_integrals(
156
site=site,
157
moment=induced_moments[3 *
158
current_polsite:3 * current_polsite +
159
3])
160
current_polsite += 1
161
162
E_pe = self.cppe_state.total_energy
163
164
if not elec_only:
165
V_pe = self.V_es + V_ind
166
else:
167
V_pe = V_ind
168
E_pe = self.cppe_state.energies["Polarization"]["Electronic"]
169
170
return E_pe, V_pe, self.V_es, V_ind
171
172