votca 2026-dev
Loading...
Searching...
No Matches
eeinteractor.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2020 The VOTCA Development Team
3 * (http://www.votca.org)
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License")
6 *
7 * You may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20// Local VOTCA includes
23
24namespace votca {
25namespace xtp {
26
27template <int N>
28Eigen::Matrix<double, N, 1> eeInteractor::VSiteA(
29 const StaticSite& siteA, const StaticSite& siteB) const {
30
31 const Eigen::Vector3d& posA = siteA.getPos();
32 const Eigen::Vector3d& posB = siteB.getPos();
33 Index rankB = siteB.getRank();
34 Eigen::Vector3d a =
35 posB - posA; // Vector of the distance between polar sites
36 const double R = a.norm();
37 const double fac1 = 1.0 / R;
38 a *= fac1; // unit vector pointing from A to B
39 Eigen::Matrix<double, N, 1> V;
40 V(0) = fac1 * siteB.getCharge();
41
42 if (N > 1 || rankB > 0) {
43 const double fac2 = std::pow(fac1, 2);
44 // Dipole-Charge Interaction
45 if constexpr (N > 1) {
46 V.template segment<3>(1) =
47 fac2 * a * siteB.getCharge(); // T_1alpha,00 (alpha=x,y,z)
48 }
49 // Charge-Dipole Interaction
50 if (rankB > 0) {
51 V(0) -=
52 fac2 * a.dot(siteB.Q().segment<3>(1)); // T_00,1alpha (alpha=x,y,z)
53 }
54
55 const double fac3 = std::pow(fac1, 3);
56 if (N > 1 && rankB > 0) {
57 // Dipole-Dipole Interaction
58 Eigen::Vector3d result =
59 -3 * a * (a.transpose() * siteB.Q().segment<3>(1));
60 result += siteB.Q().segment<3>(1);
61 result *= fac3;
62 V.template segment<3>(1) += result;
63 // T_1alpha,1beta // (alpha,beta=x,y,z)
64 }
65
66 if (N > 4 || rankB > 1) {
67 const double sqr3 = std::sqrt(3);
68 const AxA r(a);
69 {
70 // Quadrupole-Charge interaction
71 Eigen::Matrix<double, 1, 5> Qq;
72 Qq(0) = 1.5 * r.zz() - 0.5; // T20,00
73 Qq(1) = r.xz(); // T21c,00
74 Qq(2) = r.yz(); // T21s,000
75 Qq(3) = 0.5 * (r.xx() - r.yy()); // T22c,00
76 Qq(4) = r.xy(); // T22s,00
77 Qq.tail<4>() *= sqr3;
78 if constexpr (N > 4) {
79 V.template segment<5>(4) = fac3 * Qq.transpose() * siteB.getCharge();
80 }
81 if (rankB > 1) {
82 V(0) += fac3 * Qq * siteB.Q().segment<5>(4);
83 }
84 }
85 if ((N > 4 || rankB > 0) || (N > 1 || rankB > 1)) {
86
87 Eigen::Matrix<double, 3, 5> dQ;
88 const double fac4 = std::pow(fac1, 4);
89 // Quadrupole-Dipole Interaction
90 const Eigen::Vector3d afac = a * fac4;
91
92 dQ.col(0) = (1.5 - 7.5 * r.zz()) * afac;
93 dQ.col(0).z() += 3 * afac.z(); // T20-1beta (beta=x,y,z)
94
95 dQ.col(1) = -5 * r.xz() * afac;
96 dQ.col(1).z() += afac.x();
97 dQ.col(1).x() += afac.z(); // T21c-1beta (beta=x,y,z)
98
99 dQ.col(2) = -5 * r.yz() * afac;
100 dQ.col(2).z() += afac.y();
101 dQ.col(2).y() += afac.z();
102
103 dQ.col(3) = -2.5 * (r.xx() - r.yy()) * afac;
104 dQ.col(3).x() += afac.x();
105 dQ.col(3).y() -= afac.y(); // T22c-1beta (beta=x,y,z)
106
107 dQ.col(4) = -5 * r.xy() * afac;
108 dQ.col(4).y() += afac.x();
109 dQ.col(4).x() += afac.y(); // T22s-1beta (beta=x,y,z)
110
111 dQ.rightCols<4>() *= sqr3;
112
113 if constexpr (N > 4) {
114 V.template segment<5>(4) += dQ.transpose() * siteB.Q().segment<3>(1);
115 }
116 if (rankB > 1) {
117 V.template segment<3>(1) -= dQ * siteB.Q().segment<5>(4);
118 }
119 }
120
121 if (N > 4 && rankB > 1) {
122
123 // Quadrupole-Quadrupole Interaction
124 Eigen::Matrix<double, 5, 5> QQ;
125 QQ(0, 0) = 0.75 * ((35 * r.zz() - 30) * r.zz() + 3); // T20,20
126 double temp = 0.5 * sqr3 * (35 * r.zz() - 15);
127 QQ(1, 0) = temp * r.xz(); // T20,21c
128 QQ(2, 0) = temp * r.yz(); // T20,21s
129
130 temp = 5 * (7 * r.zz() - 1);
131 QQ(3, 0) = sqr3 * 0.25 * temp * (r.xx() - r.yy()); // T20,22c
132 QQ(4, 0) = sqr3 * 0.5 * temp * r.xy(); // T20,22s
133 QQ(1, 1) =
134 35 * r.zz() * r.xx() - 5 * (r.xx() + r.zz()) + 1; // T21c,21c
135 QQ(2, 1) = r.xy() * temp; // T21c,21s
136 QQ(3, 1) = 0.5 * r.xz() * (35 * (r.xx() - r.yy()) - 10); // T21c,22c
137 QQ(4, 1) = r.yz() * 5 * (7 * r.xx() - 1); // T21c,22s
138 QQ(2, 2) =
139 5 * (7 * r.yy() * r.zz() - (r.yy() + r.zz())) + 1; // T21s,21s
140 QQ(3, 2) = 0.5 * r.yz() * (35 * (r.xx() - r.yy()) + 10); // T21s,22c
141 QQ(4, 2) = r.xz() * 5 * (7 * r.yy() - 1); // T21s,22s
142 QQ(3, 3) = 8.75 * std::pow(r.xx() - r.yy(), 2) - 5 * (r.xx() + r.yy()) +
143 1; // T22c,22c
144 QQ(4, 3) = 17.5 * r.xy() * (r.xx() - r.yy()); // T22c,22s
145 QQ(4, 4) =
146 5 * (7 * r.xx() * r.yy() - (r.xx() + r.yy())) + 1; // T22s,22s
147
148 const double fac5 = std::pow(fac1, 5);
149 V.template segment<5>(4) +=
150 fac5 * QQ.selfadjointView<Eigen::Lower>() * siteB.Q().segment<5>(4);
151 }
152 }
153 }
154 return V;
155}
156
158 const PolarSite& site1, const PolarSite& site2) const {
159
160 const Eigen::Vector3d& posB = site2.getPos();
161 const Eigen::Vector3d& posA = site1.getPos();
162 Eigen::Vector3d a =
163 posB - posA; // Vector of the distance between polar sites
164 const double R = a.norm(); // Norm of distance vector
165 const double fac1 = 1 / R;
166 a *= fac1; // unit vector pointing from A to B
167
168 double lambda3 = std::pow(fac1, 3);
169 double lambda5 = lambda3;
170 const double au3 = expdamping_ * std::pow(R, 3) *
171 site1.getSqrtInvEigenDamp() *
172 site2.getSqrtInvEigenDamp(); // au3 is dimensionless
173 if (au3 < 40) {
174 const double exp_ua = std::exp(-au3);
175 lambda3 *= (1 - exp_ua);
176 lambda5 *= (1 - (1 + au3) * exp_ua);
177 }
178 Eigen::Matrix3d result = -3 * lambda5 * a * a.transpose();
179 result.diagonal().array() += lambda3;
180 return result; // T_1alpha,1beta (alpha,beta=x,y,z)
181}
182
184 const PolarSite& site1, const PolarSite& site2) const {
185 // Thole sites among themselves: the Thole model, unchanged. A pair with a
186 // charge-response site: Gaussian dipoles (width sqrt(s1^2 + s2^2)), or
187 // Thole damping if that site asks for it.
188 if (!site1.isChargeResponse() && !site2.isChargeResponse()) {
189 return FillTholeInteraction(site1, site2);
190 }
191 if ((site1.isChargeResponse() && site1.CRTholeDipoles()) ||
192 (site2.isChargeResponse() && site2.CRTholeDipoles())) {
193 return FillTholeInteraction(site1, site2);
194 }
195 const Eigen::Vector3d r = site2.getPos() - site1.getPos();
196 return GaussianKernel(r.norm(), PairWidth(site1, site2)).DipoleTensor(r);
197}
198
199double eeInteractor::PairWidth(const PolarSite& site1, const PolarSite& site2) {
200 return std::hypot(site1.isChargeResponse() ? site1.CRWidth() : 0.0,
201 site2.isChargeResponse() ? site2.CRWidth() : 0.0);
202}
203
204template <enum Estatic CE>
206 PolarSite& site2) const {
207 Eigen::Vector3d V = Eigen::Vector3d::Zero();
208 double e = 0.0;
209 double phi = 0.0;
210 if (site2.getRank() < 2) {
211 const Eigen::Vector4d V_full = VSiteA<4>(site2, site1);
212 V = V_full.segment<3>(1);
213 phi = V_full(0);
214 e = V_full.dot(site2.Q().head<4>());
215 } else {
216 const Eigen::Matrix<double, 9, 1> V_full = VSiteA<9>(site2, site1);
217 V = V_full.segment<3>(1);
218 phi = V_full(0);
219 e = V_full.dot(site2.Q());
220 }
221
222 if (CE == Estatic::noE_V) {
223 site2.V_noE() += V;
224 } else {
225 site2.V() += V;
226 }
227 // permanent multipoles act on charge-response sites as points
228 if (site2.isChargeResponse()) {
229 if (CE == Estatic::noE_V) {
230 site2.Phi_noE() += phi;
231 } else {
232 site2.Phi() += phi;
233 }
234 }
235 return e;
236}
237
238template <enum Estatic CE>
240 PolarSite& site2) const {
241 const Eigen::Matrix3d tTab = FillDipoleInteraction(site1, site2);
242 Eigen::Vector3d V = tTab.transpose() * site1.Induced_Dipole();
243 double phi = 0.0;
244 if (site1.isChargeResponse() || site2.isChargeResponse()) {
245 const Eigen::Vector3d r = site2.getPos() - site1.getPos();
246 const GaussianKernel k(r.norm(), PairWidth(site1, site2));
247 // gradient of the potential of the induced charge of site1 at site2
248 V -= site1.Induced_Charge() * k.B1 * r;
249 if (site2.isChargeResponse()) {
250 phi =
251 site1.Induced_Charge() * k.B0 + k.B1 * site1.Induced_Dipole().dot(r);
252 }
253 }
254 double e = 0.0;
255 if (CE == Estatic::noE_V) {
256 site2.V_noE() += V;
257 site2.Phi_noE() += phi;
258 } else {
259 site2.V() += V;
260 site2.Phi() += phi;
261 e = CalcPolar_stat_Energy_site(site1, site2);
262 }
263 return e;
264}
265
267 const StaticSite& site2) const {
268 double e = 0.0;
269 if (site2.getRank() < 2) {
270 const Eigen::Vector4d V_full = VSiteA<4>(site2, site1);
271 e = V_full.dot(site2.Q().head<4>());
272 } else {
273 const Eigen::Matrix<double, 9, 1> V_full = VSiteA<9>(site2, site1);
274 e = V_full.dot(site2.Q());
275 }
276 return e;
277}
278
280 const StaticSite& site2) const {
281 const Eigen::Vector4d V_full = VSiteA<4>(site1, site2);
282 return V_full.tail<3>().dot(site1.Induced_Dipole()) +
283 V_full(0) * site1.Induced_Charge();
284}
285
287 const PolarSite& site1, const StaticSite& site2) const {
289 val.E_indu_stat() = CalcPolar_stat_Energy_site(site1, site2);
290 return val;
291}
292
294 const PolarSite& site1, const PolarSite& site2) const {
295 // contributions are stat-induced, induced-stat and induced induced
297 val.E_indu_indu() = site1.Induced_Dipole().transpose() *
298 FillDipoleInteraction(site1, site2) *
299 site2.Induced_Dipole();
300 if (site1.isChargeResponse() || site2.isChargeResponse()) {
301 const Eigen::Vector3d r = site2.getPos() - site1.getPos();
302 const GaussianKernel k(r.norm(), PairWidth(site1, site2));
303 val.E_indu_indu() +=
304 site1.Induced_Charge() * site2.Induced_Charge() * k.B0 +
305 k.B1 * (site2.Induced_Charge() * site1.Induced_Dipole().dot(r) -
306 site1.Induced_Charge() * site2.Induced_Dipole().dot(r));
307 }
308 val.E_indu_stat() = CalcPolar_stat_Energy_site(site1, site2);
309 val.E_indu_stat() += CalcPolar_stat_Energy_site(site2, site1);
310 return val;
311}
312
313template <class T, enum Estatic CE>
314double eeInteractor::ApplyStaticField(const T& segment1,
315 PolarSegment& segment2) const {
316 double e = 0.0;
317 for (PolarSite& s2 : segment2) {
318 for (const auto& s1 : segment1) {
319 e += ApplyStaticField_site<CE>(s1, s2);
320 }
321 }
322 return e;
323}
324
326 const StaticSegment& seg1, PolarSegment& seg2) const;
328 const StaticSegment& seg1, PolarSegment& seg2) const;
330 const PolarSegment& seg1, PolarSegment& seg2) const;
332 const PolarSegment& seg1, PolarSegment& seg2) const;
333
334template <enum Estatic CE>
336 PolarSegment& segment2) const {
337 double e = 0;
338 for (PolarSite& s2 : segment2) {
339 for (const PolarSite& s1 : segment1) {
340 e += ApplyInducedField_site<CE>(s1, s2);
341 }
342 }
343 return e;
344}
345
347 const PolarSegment& seg1, PolarSegment& seg2) const;
349 const PolarSegment& seg1, PolarSegment& seg2) const;
350
351template <class S1, class S2>
352double eeInteractor::CalcStaticEnergy(const S1& segment1,
353 const S2& segment2) const {
354 double e = 0;
355 for (const auto& s1 : segment2) {
356 for (const auto& s2 : segment1) {
357 e += CalcStaticEnergy_site(s2, s1);
358 }
359 }
360 return e;
361}
362
363template double eeInteractor::CalcStaticEnergy(const StaticSegment& seg1,
364 const PolarSegment& seg2) const;
365template double eeInteractor::CalcStaticEnergy(const StaticSegment& seg1,
366 const StaticSegment& seg2) const;
367template double eeInteractor::CalcStaticEnergy(const PolarSegment& seg1,
368 const PolarSegment& seg2) const;
369template double eeInteractor::CalcStaticEnergy(const PolarSegment& seg1,
370 const StaticSegment& seg2) const;
371template <class S>
373 double e = 0;
374 for (Index i = 0; i < seg.size(); i++) {
375 for (Index j = 0; j < i; j++) {
376 e += CalcStaticEnergy_site(seg[i], seg[j]);
377 }
378 }
379 return e;
380}
382 const PolarSegment& seg1) const;
384 const StaticSegment& seg2) const;
385
386template <class S1, class S2>
388 const S2& segment2) const {
390 for (const auto& s1 : segment2) {
391 for (const auto& s2 : segment1) {
392 e += CalcPolarEnergy_site(s2, s1);
393 }
394 }
395 return e;
396}
397
399 const PolarSegment& seg1, const PolarSegment& seg2) const;
401 const PolarSegment& seg1, const StaticSegment& seg2) const;
402
404 const PolarSegment& seg) const {
405 double e = 0;
406 if (seg.hasChargeResponse()) {
407 // Pi contains the molecule's own screening: no couplings inside
408 return e;
409 }
410 for (Index i = 0; i < seg.size(); i++) {
411 for (Index j = 0; j < i; j++) {
412 e += seg[i].Induced_Dipole().transpose() *
413 FillTholeInteraction(seg[i], seg[j]) * seg[j].Induced_Dipole();
414 }
415 }
416 return e;
417}
418
420 const PolarSegment& seg) const {
421 if (seg.hasChargeResponse()) {
422 throw std::runtime_error(
423 "Cholesky_IntraSegment: segment " + seg.getType() +
424 " has a charge response; it is solved with its region (PolarRegion)");
425 }
426 Index size = 3 * seg.size();
427
428 Eigen::MatrixXd A = Eigen::MatrixXd::Zero(size, size);
429 for (Index i = 1; i < seg.size(); i++) {
430 for (Index j = 0; j < i; j++) {
431 A.block<3, 3>(3 * i, 3 * j) = FillTholeInteraction(seg[i], seg[j]);
432 }
433 }
434
435 for (Index i = 0; i < seg.size(); i++) {
436 A.block<3, 3>(3 * i, 3 * i) = seg[i].getPInv();
437 }
438 Eigen::VectorXd b = Eigen::VectorXd(size);
439 for (Index i = 0; i < seg.size(); i++) {
440 const Eigen::Vector3d V = seg[i].V() + seg[i].V_noE();
441 b.segment<3>(3 * i) = -V;
442 }
443
444 Eigen::LLT<Eigen::Ref<Eigen::MatrixXd>> lltOfA(A);
445 return lltOfA.solve(b);
446}
447
448} // namespace xtp
449} // namespace votca
const std::string & getType() const
const double & zz() const
Definition eigen.h:120
const double & yz() const
Definition eigen.h:119
const double & yy() const
Definition eigen.h:118
const double & xy() const
Definition eigen.h:116
const double & xz() const
Definition eigen.h:117
const double & xx() const
Definition eigen.h:115
Class to represent Atom/Site in electrostatic+polarization.
Definition polarsite.h:36
double getSqrtInvEigenDamp() const
Definition polarsite.h:61
const Eigen::Vector3d & Induced_Dipole() const
Definition polarsite.h:121
double CRWidth() const
Definition polarsite.h:107
const Eigen::Vector3d & V_noE() const
Definition polarsite.h:72
bool CRTholeDipoles() const
Definition polarsite.h:108
double Phi_noE() const
Definition polarsite.h:80
bool isChargeResponse() const
Definition polarsite.h:106
const Eigen::Vector3d & V() const
Definition polarsite.h:68
double Induced_Charge() const
Definition polarsite.h:114
double Phi() const
Definition polarsite.h:78
Class to represent Atom/Site in electrostatic.
Definition staticsite.h:37
const Eigen::Vector3d & getPos() const
Definition staticsite.h:80
double getCharge() const
Definition staticsite.h:122
Index getRank() const
Definition staticsite.h:78
const Vector9d & Q() const
Definition staticsite.h:123
double ApplyStaticField_site(const StaticSite &site1, PolarSite &site2) const
double ApplyStaticField(const T &segment1, PolarSegment &segment2) const
Eigen::Matrix3d FillDipoleInteraction(const PolarSite &site1, const PolarSite &site2) const
double ApplyInducedField(const PolarSegment &segment1, PolarSegment &segment2) const
double CalcStaticEnergy_site(const StaticSite &site1, const StaticSite &site2) const
Eigen::Matrix< double, N, 1 > VSiteA(const StaticSite &site1, const StaticSite &site2) const
E_terms CalcPolarEnergy(const S1 &segment1, const S2 &segment2) const
Eigen::VectorXd Cholesky_IntraSegment(const PolarSegment &seg) const
double CalcPolar_stat_Energy_site(const PolarSite &site1, const StaticSite &site2) const
E_terms CalcPolarEnergy_site(const PolarSite &site1, const StaticSite &site2) const
static double PairWidth(const PolarSite &site1, const PolarSite &site2)
Eigen::Matrix3d FillTholeInteraction(const PolarSite &site1, const PolarSite &site2) const
double CalcStaticEnergy_IntraSegment(const S &seg) const
double ApplyInducedField_site(const PolarSite &site1, PolarSite &site2) const
double CalcStaticEnergy(const S1 &segment1, const S2 &segment2) const
double CalcPolarEnergy_IntraSegment(const PolarSegment &seg) const
Charge transport classes.
Definition ERIs.h:28
ClassicalSegment< PolarSite > PolarSegment
ClassicalSegment< StaticSite > StaticSegment
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26
Interaction of two normalized Gaussian charge distributions exp(-r^2/s_i^2) and exp(-r^2/s_j^2): erf(...
Eigen::Matrix3d DipoleTensor(const Eigen::Vector3d &r) const