votca 2026-dev
Loading...
Searching...
No Matches
ewaldshapecorrection.h
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#pragma once
21#ifndef VOTCA_XTP_EWALDSHAPECORRECTION_H
22#define VOTCA_XTP_EWALDSHAPECORRECTION_H
23
24// Standard includes
25#include <utility>
26#include <vector>
27
28// Local VOTCA includes
29#include "eeinteractor.h"
30#include "eigen.h"
31#include "ewaldregistry.h"
32
64
65namespace votca {
66namespace xtp {
67
68enum class EwaldShape { Cube, Slab };
69
71 public:
72 EwaldShapeCorrection(double volume, const EwaldRegistry& registry,
73 EwaldShape shape);
74
75 // Adds the (position-independent) depolarizing field into target's own
76 // V()/V_noE() accumulator, generated by every segment in the registry
77 // at charge state source_state (including target's own segment, if
78 // registered -- see class documentation).
79 template <enum Estatic CE>
80 void AddFieldAt(PolarSite& target, EwaldChargeState source_state) const;
81
82 // Shape/surface contribution to the PERMANENT-multipole interaction
83 // energy between a supplied foreground (1) and the rest of the cell
84 // (2). With
85 //
86 // Q0 = sum_i q_i (net charge)
87 // Q1 = sum_i (q_i r_i + mu_i) (dipole moment)
88 // Q2 = sum_i (0.5 q_i r_i r_i^T + mu_i r_i^T) (second moment)
89 //
90 // the cube/sphere and slab forms are
91 //
92 // E = -(4*pi/(3*V)) * [ Q0_1 TrQ2_2 + Q0_2 TrQ2_1 - Q1_1 . Q1_2 ]
93 // E = -(4*pi/V) * [ Q0_1 Q2_2zz + Q0_2 Q2_1zz - Q1_1z Q1_2z ]
94 //
95 // THE SECOND-MOMENT TERMS ARE NOT OPTIONAL. An earlier version of this
96 // method kept only the Q1.Q1 piece, reasoning that the shape energy is
97 // the shape FIELD contracted with the foreground's moments. That
98 // reasoning holds only for a NEUTRAL foreground. As soon as Q0_1 is
99 // nonzero, Q1_1 depends on where the origin is put, and so did that
100 // energy -- which is a statement about a missing term, not about
101 // physics. The full expression above is exactly origin-invariant:
102 // under r -> r + a,
103 //
104 // Q1 -> Q1 + Q0 a
105 // TrQ2 -> TrQ2 + Q1 . a + 0.5 Q0 a^2
106 //
107 // and the three pieces cancel to all orders in a. A unit test shifts
108 // every position by an arbitrary vector and requires the answer not to
109 // move; that test is the real guarantee here, and it fails on the
110 // Q1.Q1-only form. This matches the legacy code's own U12_ShapeTerm,
111 // which carries the same three pieces.
112 //
113 // PERMANENT moments only, on BOTH sides. The induced part of this
114 // interaction is not missing; it belongs to the polar region, which
115 // accounts for it as sum(mu_ind . V) from the field AddFieldAt
116 // delivers. Including it here as well would count it twice. That is
117 // the division of labour the real- and reciprocal-space energies
118 // already follow, and it is why TotalDipoleMoment -- which does
119 // include induced dipoles, correctly, for the FIELD -- is not reused.
120 // Legacy splits the same way, into its _pp, _pu and _uu channels; this
121 // is its _pp.
122 //
123 // Rank is capped at 1 (charge and dipole) because every other sum in
124 // this Ewald implementation is: the real-space interactor and the
125 // reciprocal structure factors both stop at the dipole. Legacy adds
126 // the sites' intrinsic quadrupoles into Q2 as well. That difference
127 // does not affect origin-invariance -- an intrinsic quadrupole is
128 // itself translation-invariant -- but it is a real numerical
129 // difference wherever the multipole files carry rank-2 moments.
130 //
131 // `background_exclusions` holds the foreground's own background copies
132 // out of the background moments, matching the suppression the other
133 // two sums apply.
135 const std::vector<std::pair<const PolarSite*, Eigen::Vector3d>>&
136 foreground,
137 const std::vector<const PolarSite*>& background_exclusions,
138 EwaldChargeState source_state) const;
139
140 // The same shape/surface cross term, with the background entering
141 // through its INDUCED dipoles instead of its permanent moments. An
142 // induced dipole carries no charge, so the background's moments
143 // reduce to
144 //
145 // Q0_bg = 0, Q1_bg = sum mu_ind, Q2_bg = sum mu_ind r^T
146 //
147 // and the bracket loses its Q0_bg TrQ2_fg piece. The foreground still
148 // contributes its permanent moments, all three of them -- Q0_fg
149 // TrQ2_bg survives and is the piece that matters for a charged
150 // foreground, exactly as in the permanent case.
151 //
152 // This is the shape partner of
153 // EwaldRealSpaceSum::CalcInducedSourceEnergyAt; see
154 // EwaldRealSpaceInteractor::CalcInducedSourceEnergy for what the term
155 // is. Undamped, like every shape contribution: this is a boundary
156 // condition on an infinite sum, not a short-range interaction.
158 const std::vector<std::pair<const PolarSite*, Eigen::Vector3d>>&
159 foreground,
160 const std::vector<const PolarSite*>& background_exclusions,
161 EwaldChargeState source_state) const;
162
163 private:
164 Eigen::Vector3d TotalDipoleMoment(EwaldChargeState source_state) const;
165
166 // The three permanent moments the shape energy is built from, to rank
167 // 1. See CalcStaticEnergyBetween for the definitions and for why all
168 // three are needed.
169 struct Moments {
170 double q0 = 0.0;
171 Eigen::Vector3d q1 = Eigen::Vector3d::Zero();
172 Eigen::Matrix3d q2 = Eigen::Matrix3d::Zero();
173 };
174
175 static void Accumulate(Moments& m, const PolarSite& site,
176 const Eigen::Vector3d& pos);
177
178 // Permanent moments of every registered site at source_state, with the
179 // listed sites held out.
181 EwaldChargeState source_state,
182 const std::vector<const PolarSite*>& exclusions) const;
183
184 // As BackgroundMoments, but built from the sites' INDUCED dipoles.
186 EwaldChargeState source_state,
187 const std::vector<const PolarSite*>& exclusions) const;
188
189 double volume_;
192};
193
194} // namespace xtp
195} // namespace votca
196
197#endif // VOTCA_XTP_EWALDSHAPECORRECTION_H
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
Provides a means for comparing floating point numbers.
Definition basebead.h:33