89 const Eigen::Matrix3d recip = 2.0 * kPi *
box_.inverse().transpose();
90 const Eigen::Vector3d b1 = recip.col(0);
91 const Eigen::Vector3d b2 = recip.col(1);
92 const Eigen::Vector3d b3 = recip.col(2);
100 std::vector<KVector> kvecs;
101 for (
Index i1 = -n1; i1 <= n1; ++i1) {
102 for (
Index i2 = -n2; i2 <= n2; ++i2) {
103 for (
Index i3 = -n3; i3 <= n3; ++i3) {
104 if (i1 == 0 && i2 == 0 && i3 == 0) {
108 Eigen::Vector3d k = double(i1) * b1 + double(i2) * b2 + double(i3) * b3;
109 double k2 = k.squaredNorm();
111 kvecs.push_back({k, k2});
122 std::vector<std::complex<double>>
S(
kvectors_.size(),
123 std::complex<double>(0.0, 0.0));
131 std::vector<double> q_flat;
132 std::vector<Eigen::Vector3d> mu_flat;
133 std::vector<Eigen::Vector3d> pos_flat;
135 if (!
registry_.Has(source_id, source_state)) {
140 q_flat.push_back(site.getCharge());
141 mu_flat.push_back(site.getStaticDipole() + site.getInducedDipole());
142 pos_flat.push_back(site.getPos());
161 const Index chunk = std::max<Index>(1, n_k / 20);
162 for (
Index k_begin = 0; k_begin < n_k; k_begin += chunk) {
163 const Index k_end = std::min(n_k, k_begin + chunk);
164#pragma omp parallel for schedule(static)
165 for (
Index idx = k_begin; idx < k_end; ++idx) {
166 const Eigen::Vector3d& k =
kvectors_[std::size_t(idx)].k;
167 std::complex<double> acc(0.0, 0.0);
168 for (
Index n = 0; n < n_sites; ++n) {
169 const double kr = k.dot(pos_flat[std::size_t(n)]);
172 const std::complex<double> phase = std::polar(1.0, -kr);
173 const double k_dot_mu = k.dot(mu_flat[std::size_t(n)]);
174 acc += std::complex<double>(q_flat[std::size_t(n)], -k_dot_mu) * phase;
176 S[std::size_t(idx)] = acc;
179 progress(std::size_t(k_end), std::size_t(n_k));
186 const std::vector<std::pair<const PolarSite*, Eigen::Vector3d>>& foreground,
187 const std::vector<const PolarSite*>& background_exclusions,
199 std::vector<double> q_fg;
200 std::vector<Eigen::Vector3d> mu_fg;
201 std::vector<Eigen::Vector3d> pos_fg;
202 q_fg.reserve(foreground.size());
203 mu_fg.reserve(foreground.size());
204 pos_fg.reserve(foreground.size());
205 for (
const auto& entry : foreground) {
209 pos_fg.push_back(entry.second);
215 const std::vector<const PolarSite*>& fg_sites = background_exclusions;
216 std::vector<double> q_bg;
217 std::vector<Eigen::Vector3d> mu_bg;
218 std::vector<Eigen::Vector3d> pos_bg;
220 if (!
registry_.Has(source_id, source_state)) {
225 bool is_foreground =
false;
228 is_foreground =
true;
235 q_bg.push_back(site.getCharge());
236 mu_bg.push_back(site.getStaticDipole());
237 pos_bg.push_back(site.getPos());
244 const double prefactor = 4.0 * kPi /
volume_;
251#pragma omp parallel for schedule(static) reduction(+ : energy)
252 for (
Index idx = 0; idx < n_k; ++idx) {
253 const Eigen::Vector3d& k =
kvectors_[std::size_t(idx)].k;
254 const double k2 =
kvectors_[std::size_t(idx)].k2;
256 std::complex<double> s_fg(0.0, 0.0);
257 for (
Index n = 0; n < n_fg; ++n) {
258 const double kr = k.dot(pos_fg[std::size_t(n)]);
259 const std::complex<double> phase = std::polar(1.0, -kr);
260 const double k_dot_mu = k.dot(mu_fg[std::size_t(n)]);
261 s_fg += std::complex<double>(q_fg[std::size_t(n)], -k_dot_mu) * phase;
264 std::complex<double> s_bg(0.0, 0.0);
265 for (
Index n = 0; n < n_bg; ++n) {
266 const double kr = k.dot(pos_bg[std::size_t(n)]);
267 const std::complex<double> phase = std::polar(1.0, -kr);
268 const double k_dot_mu = k.dot(mu_bg[std::size_t(n)]);
269 s_bg += std::complex<double>(q_bg[std::size_t(n)], -k_dot_mu) * phase;
272 const double weight = std::exp(-k2 / (4.0 *
alpha_ *
alpha_)) / k2;
273 energy += prefactor * weight * (std::conj(s_fg) * s_bg).real();
279 const std::vector<std::pair<const PolarSite*, Eigen::Vector3d>>& foreground,
280 const std::vector<const PolarSite*>& background_exclusions,
288 std::vector<double> q_fg;
289 std::vector<Eigen::Vector3d> mu_fg;
290 std::vector<Eigen::Vector3d> pos_fg;
291 q_fg.reserve(foreground.size());
292 mu_fg.reserve(foreground.size());
293 pos_fg.reserve(foreground.size());
294 for (
const auto& entry : foreground) {
298 pos_fg.push_back(entry.second);
304 std::vector<Eigen::Vector3d> mu_bg;
305 std::vector<Eigen::Vector3d> pos_bg;
307 if (!
registry_.Has(source_id, source_state)) {
312 bool is_excluded =
false;
313 for (
const PolarSite* skip : background_exclusions) {
322 mu_bg.push_back(site.getInducedDipole());
323 pos_bg.push_back(site.getPos());
330 const double prefactor = 4.0 * kPi /
volume_;
333#pragma omp parallel for schedule(static) reduction(+ : energy)
334 for (
Index idx = 0; idx < n_k; ++idx) {
335 const Eigen::Vector3d& k =
kvectors_[std::size_t(idx)].k;
336 const double k2 =
kvectors_[std::size_t(idx)].k2;
338 std::complex<double> s_fg(0.0, 0.0);
339 for (
Index n = 0; n < n_fg; ++n) {
340 const double kr = k.dot(pos_fg[std::size_t(n)]);
341 const std::complex<double> phase = std::polar(1.0, -kr);
342 const double k_dot_mu = k.dot(mu_fg[std::size_t(n)]);
343 s_fg += std::complex<double>(q_fg[std::size_t(n)], -k_dot_mu) * phase;
346 std::complex<double> s_bg(0.0, 0.0);
347 for (
Index n = 0; n < n_bg; ++n) {
348 const double kr = k.dot(pos_bg[std::size_t(n)]);
349 const std::complex<double> phase = std::polar(1.0, -kr);
350 const double k_dot_mu = k.dot(mu_bg[std::size_t(n)]);
351 s_bg += std::complex<double>(0.0, -k_dot_mu) * phase;
354 const double weight = std::exp(-k2 / (4.0 *
alpha_ *
alpha_)) / k2;
355 energy += prefactor * weight * (std::conj(s_fg) * s_bg).real();
371 const std::vector<std::complex<double>>
S =
373 const double prefactor = 4.0 * kPi /
volume_;
381 const Index n_targets =
Index(targets.size());
382#pragma omp parallel for schedule(static)
383 for (
Index t_i = 0; t_i < n_targets; ++t_i) {
384 PolarSite& target = *targets[std::size_t(t_i)];
386 const Eigen::Vector3d r = target.
getPos();
387 Eigen::Vector3d field = Eigen::Vector3d::Zero();
389 for (std::size_t idx = 0; idx <
kvectors_.size(); ++idx) {
396 const std::complex<double> phase = std::polar(1.0, kv.
k.dot(r));
397 double im_part = (
S[idx] * phase).imag();
398 field += prefactor * weight * im_part * kv.
k;
402 target.
V_noE() += field;
412 const std::vector<std::complex<double>>
S =
414 const double prefactor = 4.0 * kPi /
volume_;
416 const std::size_t n_k =
kvectors_.size();
417 Eigen::VectorXd phi = Eigen::VectorXd::Zero(n_points);
428 std::vector<double> c_re(n_k);
429 std::vector<double> c_im(n_k);
430 const double inv_four_alpha2 = 1.0 / (4.0 *
alpha_ *
alpha_);
431 for (std::size_t idx = 0; idx < n_k; ++idx) {
432 const double weight = prefactor *
433 std::exp(-
kvectors_[idx].k2 * inv_four_alpha2) /
435 c_re[idx] = weight *
S[idx].real();
436 c_im[idx] = weight *
S[idx].imag();
443#pragma omp parallel for schedule(static)
444 for (
Index p = 0; p < n_points; ++p) {
445 const Eigen::Vector3d& r = points[std::size_t(p)];
447 for (std::size_t idx = 0; idx < n_k; ++idx) {
451 const double theta =
kvectors_[idx].k.dot(r);
452 acc += c_re[idx] * std::cos(theta) - c_im[idx] * std::sin(theta);