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 return StaticShapeEnergy(fg, bg);
114}
115
117 const Moments& bg) const {
118 if (shape_ == EwaldShape::Cube) {
119 const double bracket =
120 fg.q0 * bg.q2.trace() + bg.q0 * fg.q2.trace() - fg.q1.dot(bg.q1);
121 return -(4.0 * kPi / (3.0 * volume_)) * bracket;
122 }
123 const double bracket =
124 fg.q0 * bg.q2(2, 2) + bg.q0 * fg.q2(2, 2) - fg.q1.z() * bg.q1.z();
125 return -(4.0 * kPi / volume_) * bracket;
126}
127
129 const Moments& bg) const {
130 // bg.q0 is zero by construction (dipoles only), so its term is dropped.
131 if (shape_ == EwaldShape::Cube) {
132 const double bracket = fg.q0 * bg.q2.trace() - fg.q1.dot(bg.q1);
133 return -(4.0 * kPi / (3.0 * volume_)) * bracket;
134 }
135 const double bracket = fg.q0 * bg.q2(2, 2) - fg.q1.z() * bg.q1.z();
136 return -(4.0 * kPi / volume_) * bracket;
137}
138
140 const std::vector<Eigen::Vector3d>& points,
141 EwaldChargeState source_state) const {
142 const std::vector<const PolarSite*> no_exclusions;
143 const Moments bg = BackgroundMoments(source_state, no_exclusions);
144 const Moments bg_induced =
145 BackgroundInducedMoments(source_state, no_exclusions);
146 const Index n = Index(points.size());
147 Eigen::VectorXd phi(n);
148#pragma omp parallel for schedule(static)
149 for (Index p = 0; p < n; ++p) {
150 // a unit charge at the point, as Accumulate would record it
151 const Eigen::Vector3d& pos = points[std::size_t(p)];
152 Moments fg;
153 fg.q0 = 1.0;
154 fg.q1 = pos;
155 fg.q2 = 0.5 * pos * pos.transpose();
156 phi[p] = StaticShapeEnergy(fg, bg) + InducedShapeEnergy(fg, bg_induced);
157 }
158 return phi;
159}
160
162 EwaldChargeState source_state,
163 const std::vector<const PolarSite*>& exclusions) const {
164 Moments m;
165 for (Index id : registry_.AllIds()) {
166 if (!registry_.Has(id, source_state)) {
167 continue;
168 }
169 const PolarSegment& segment = registry_.Get(id, source_state);
170 for (const PolarSite& site : segment) {
171 bool excluded = false;
172 for (const PolarSite* skip : exclusions) {
173 if (skip == &site) {
174 excluded = true;
175 break;
176 }
177 }
178 if (excluded) {
179 continue;
180 }
181 // Induced dipole only: no charge, hence no q0 and no 0.5*q*r*r^T.
182 const Eigen::Vector3d mu = site.getInducedDipole();
183 m.q1 += mu;
184 m.q2 += mu * site.getPos().transpose();
185 }
186 }
187 return m;
188}
189
191 const std::vector<std::pair<const PolarSite*, Eigen::Vector3d>>& foreground,
192 const std::vector<const PolarSite*>& background_exclusions,
193 EwaldChargeState source_state) const {
194 // See this method's own declaration.
195 Moments fg;
196 for (const auto& entry : foreground) {
197 Accumulate(fg, *entry.first, entry.second);
198 }
199
200 const Moments bg =
201 BackgroundInducedMoments(source_state, background_exclusions);
202 return InducedShapeEnergy(fg, bg);
203}
204
205template <enum Estatic CE>
207 EwaldChargeState source_state) const {
208 const Eigen::Vector3d M = TotalDipoleMoment(source_state);
209
210 Eigen::Vector3d field;
211 if (shape_ == EwaldShape::Cube) {
212 field = -(4.0 * kPi / (3.0 * volume_)) * M;
213 } else {
214 field = Eigen::Vector3d::Zero();
215 field.z() = -(4.0 * kPi / volume_) * M.z();
216 }
217
218 if (CE == Estatic::noE_V) {
219 target.V_noE() += field;
220 } else {
221 target.V() += field;
222 }
223}
224
229
230} // namespace xtp
231} // 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)
Eigen::VectorXd PotentialAtMany(const std::vector< Eigen::Vector3d > &points, EwaldChargeState source_state) const
double StaticShapeEnergy(const Moments &fg, const Moments &bg) const
double InducedShapeEnergy(const Moments &fg, const Moments &bg) const
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