votca 2026-dev
Loading...
Searching...
No Matches
ewaldshapecorrection.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2026 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// Standard includes
21#include <cmath>
22
23// Local VOTCA includes
25
26namespace votca {
27namespace xtp {
28
29namespace {
30constexpr double kPi = 3.14159265358979323846;
31} // namespace
32
34 const EwaldRegistry& registry,
35 EwaldShape shape)
36 : volume_(volume), registry_(registry), shape_(shape) {}
37
39 EwaldChargeState source_state) const {
40 Eigen::Vector3d M = Eigen::Vector3d::Zero();
41 for (Index id : registry_.AllIds()) {
42 if (!registry_.Has(id, source_state)) {
43 continue;
44 }
45 const PolarSegment& segment = registry_.Get(id, source_state);
46 for (const PolarSite& site : segment) {
47 M += site.getCharge() * site.getPos();
48 M += site.getStaticDipole();
49 M += site.getInducedDipole();
50 }
51 }
52 return M;
53}
54
56 const Eigen::Vector3d& pos) {
57 // Rank 1: charge and dipole. See CalcStaticEnergyBetween for why the
58 // sites' intrinsic quadrupoles are not folded in here, and for what
59 // that costs relative to legacy.
60 const double q = site.getCharge();
61 const Eigen::Vector3d mu = site.getStaticDipole();
62 m.q0 += q;
63 m.q1 += q * pos;
64 m.q1 += mu;
65 m.q2 += 0.5 * q * pos * pos.transpose();
66 m.q2 += mu * pos.transpose();
67}
68
70 EwaldChargeState source_state,
71 const std::vector<const PolarSite*>& exclusions) const {
72 Moments m;
73 for (Index id : registry_.AllIds()) {
74 if (!registry_.Has(id, source_state)) {
75 continue;
76 }
77 const PolarSegment& segment = registry_.Get(id, source_state);
78 for (const PolarSite& site : segment) {
79 // Identity by address, exactly as the reciprocal-space cross
80 // energy does it, so a site held out there is held out here too.
81 bool excluded = false;
82 for (const PolarSite* skip : exclusions) {
83 if (skip == &site) {
84 excluded = true;
85 break;
86 }
87 }
88 if (excluded) {
89 continue;
90 }
91 Accumulate(m, site, site.getPos());
92 }
93 }
94 return m;
95}
96
98 const std::vector<std::pair<const PolarSite*, Eigen::Vector3d>>& foreground,
99 const std::vector<const PolarSite*>& background_exclusions,
100 EwaldChargeState source_state) const {
101 // See this method's own declaration for the formula, for why all three
102 // moments are needed, and for the permanent-only convention.
103
104 // The foreground's moments come from the sites themselves -- so in the
105 // job's OWN charge state -- but at the positions supplied, which are
106 // the positions those sites actually occupy.
107 Moments fg;
108 for (const auto& entry : foreground) {
109 Accumulate(fg, *entry.first, entry.second);
110 }
111
112 const Moments bg = BackgroundMoments(source_state, background_exclusions);
113
114 if (shape_ == EwaldShape::Cube) {
115 const double bracket =
116 fg.q0 * bg.q2.trace() + bg.q0 * fg.q2.trace() - fg.q1.dot(bg.q1);
117 return -(4.0 * kPi / (3.0 * volume_)) * bracket;
118 }
119 const double bracket =
120 fg.q0 * bg.q2(2, 2) + bg.q0 * fg.q2(2, 2) - fg.q1.z() * bg.q1.z();
121 return -(4.0 * kPi / volume_) * bracket;
122}
123
125 EwaldChargeState source_state,
126 const std::vector<const PolarSite*>& exclusions) const {
127 Moments m;
128 for (Index id : registry_.AllIds()) {
129 if (!registry_.Has(id, source_state)) {
130 continue;
131 }
132 const PolarSegment& segment = registry_.Get(id, source_state);
133 for (const PolarSite& site : segment) {
134 bool excluded = false;
135 for (const PolarSite* skip : exclusions) {
136 if (skip == &site) {
137 excluded = true;
138 break;
139 }
140 }
141 if (excluded) {
142 continue;
143 }
144 // Induced dipole only: no charge, hence no q0 and no 0.5*q*r*r^T.
145 const Eigen::Vector3d mu = site.getInducedDipole();
146 m.q1 += mu;
147 m.q2 += mu * site.getPos().transpose();
148 }
149 }
150 return m;
151}
152
154 const std::vector<std::pair<const PolarSite*, Eigen::Vector3d>>& foreground,
155 const std::vector<const PolarSite*>& background_exclusions,
156 EwaldChargeState source_state) const {
157 // See this method's own declaration.
158 Moments fg;
159 for (const auto& entry : foreground) {
160 Accumulate(fg, *entry.first, entry.second);
161 }
162
163 const Moments bg =
164 BackgroundInducedMoments(source_state, background_exclusions);
165
166 // bg.q0 is zero by construction, so its term is dropped rather than
167 // written out and multiplied by zero.
168 if (shape_ == EwaldShape::Cube) {
169 const double bracket = fg.q0 * bg.q2.trace() - fg.q1.dot(bg.q1);
170 return -(4.0 * kPi / (3.0 * volume_)) * bracket;
171 }
172 const double bracket = fg.q0 * bg.q2(2, 2) - fg.q1.z() * bg.q1.z();
173 return -(4.0 * kPi / volume_) * bracket;
174}
175
176template <enum Estatic CE>
178 EwaldChargeState source_state) const {
179 const Eigen::Vector3d M = TotalDipoleMoment(source_state);
180
181 Eigen::Vector3d field;
182 if (shape_ == EwaldShape::Cube) {
183 field = -(4.0 * kPi / (3.0 * volume_)) * M;
184 } else {
185 field = Eigen::Vector3d::Zero();
186 field.z() = -(4.0 * kPi / volume_) * M.z();
187 }
188
189 if (CE == Estatic::noE_V) {
190 target.V_noE() += field;
191 } else {
192 target.V() += field;
193 }
194}
195
200
201} // namespace xtp
202} // namespace votca
EwaldShapeCorrection(double volume, const EwaldRegistry &registry, EwaldShape shape)
double CalcStaticEnergyBetween(const std::vector< std::pair< const PolarSite *, Eigen::Vector3d > > &foreground, const std::vector< const PolarSite * > &background_exclusions, EwaldChargeState source_state) const
Moments BackgroundMoments(EwaldChargeState source_state, const std::vector< const PolarSite * > &exclusions) const
static void Accumulate(Moments &m, const PolarSite &site, const Eigen::Vector3d &pos)
Moments BackgroundInducedMoments(EwaldChargeState source_state, const std::vector< const PolarSite * > &exclusions) const
double CalcInducedSourceEnergyBetween(const std::vector< std::pair< const PolarSite *, Eigen::Vector3d > > &foreground, const std::vector< const PolarSite * > &background_exclusions, EwaldChargeState source_state) const
void AddFieldAt(PolarSite &target, EwaldChargeState source_state) const
Eigen::Vector3d TotalDipoleMoment(EwaldChargeState source_state) const
Class to represent Atom/Site in electrostatic+polarization.
Definition polarsite.h:36
const Eigen::Vector3d & V_noE() const
Definition polarsite.h:72
Eigen::Vector3d getStaticDipole() const final
Definition polarsite.cc:58
const Eigen::Vector3d & V() const
Definition polarsite.h:68
double getCharge() const
Definition staticsite.h:122
Charge transport classes.
Definition ERIs.h:28
ClassicalSegment< PolarSite > PolarSegment
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26