ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
dp3m.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2010-2022 The ESPResSo project
3 * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010
4 * Max-Planck-Institute for Polymer Research, Theory Group
5 *
6 * This file is part of ESPResSo.
7 *
8 * ESPResSo is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * ESPResSo is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22/** @file
23 * P3M algorithm for long-range magnetic dipole-dipole interaction.
24 *
25 * By default the magnetic epsilon is metallic = 0.
26 */
27
28#include "config/config.hpp"
29
30#ifdef DP3M
31
33
35#include "p3m/TuningLogger.hpp"
36#include "p3m/common.hpp"
37#include "p3m/fft.hpp"
39#include "p3m/interpolation.hpp"
40#include "p3m/send_mesh.hpp"
41
42#include "BoxGeometry.hpp"
43#include "LocalBox.hpp"
44#include "Particle.hpp"
45#include "ParticleRange.hpp"
46#include "PropagationMode.hpp"
49#include "communication.hpp"
50#include "errorhandling.hpp"
52#include "npt.hpp"
53#include "system/System.hpp"
54#include "tuning.hpp"
55
56#include <utils/Vector.hpp>
57#include <utils/constants.hpp>
60#include <utils/math/sinc.hpp>
61#include <utils/math/sqr.hpp>
62
63#include <boost/mpi/collectives/all_reduce.hpp>
64#include <boost/mpi/collectives/reduce.hpp>
65
66#include <algorithm>
67#include <array>
68#include <cstddef>
69#include <cstdio>
70#include <functional>
71#include <optional>
72#include <sstream>
73#include <stdexcept>
74#include <vector>
75
77 int local_n = 0;
78 double local_mu2 = 0.;
79
80 for (auto const &p : get_system().cell_structure->local_particles()) {
81 if (p.dipm() != 0.) {
82 local_mu2 += p.calc_dip().norm2();
83 local_n++;
84 }
85 }
86
87 boost::mpi::all_reduce(comm_cart, local_mu2, dp3m.sum_mu2, std::plus<>());
88 boost::mpi::all_reduce(comm_cart, local_n, dp3m.sum_dip_part, std::plus<>());
89}
90
91static double dp3m_k_space_error(double box_size, int mesh, int cao,
92 int n_c_part, double sum_q2, double alpha_L);
93
94static double dp3m_real_space_error(double box_size, double r_cut_iL,
95 int n_c_part, double sum_q2,
96 double alpha_L);
97
98/** Compute the value of alpha through a bisection method.
99 * Based on eq. (33) @cite wang01a.
100 */
101double dp3m_rtbisection(double box_size, double r_cut_iL, int n_c_part,
102 double sum_q2, double x1, double x2, double xacc,
103 double tuned_accuracy);
104
105double DipolarP3M::calc_average_self_energy_k_space() const {
106 auto const start = Utils::Vector3i{dp3m.fft.plan[3].start};
107 auto const size = Utils::Vector3i{dp3m.fft.plan[3].new_mesh};
108
109 auto const &box_geo = *get_system().box_geo;
110 auto const node_phi = grid_influence_function_self_energy(
111 dp3m.params, start, start + size, dp3m.g_energy);
112
113 double phi = 0.;
114 boost::mpi::reduce(comm_cart, node_phi, phi, std::plus<>(), 0);
115 phi /= 3. * box_geo.length()[0] * Utils::int_pow<3>(dp3m.params.mesh[0]);
116 return phi * Utils::pi();
117}
118
121 assert(dp3m.params.cao >= 1 and dp3m.params.cao <= 7);
122 assert(dp3m.params.alpha > 0.);
123
124 auto const &system = get_system();
125 auto const &box_geo = *system.box_geo;
126 auto const &local_geo = *system.local_geo;
127 auto const verlet_skin = system.cell_structure->get_verlet_skin();
128
129 dp3m.params.cao3 = Utils::int_pow<3>(dp3m.params.cao);
130 dp3m.params.recalc_a_ai_cao_cut(box_geo.length());
131 dp3m.local_mesh.calc_local_ca_mesh(dp3m.params, local_geo, verlet_skin, 0.);
132
134
135 int ca_mesh_size =
139 dp3m.rs_mesh.resize(ca_mesh_size);
140 dp3m.ks_mesh.resize(ca_mesh_size);
141
142 for (auto &val : dp3m.rs_mesh_dip) {
143 val.resize(ca_mesh_size);
144 }
145
147
148 /* fix box length dependent constants */
149 scaleby_box_l();
150
152}
153
154DipolarP3M::DipolarP3M(P3MParameters &&parameters, double prefactor,
155 int tune_timings, bool tune_verbose)
156 : dp3m{std::move(parameters)}, tune_timings{tune_timings},
157 tune_verbose{tune_verbose} {
158
160 m_is_tuned = !dp3m.params.tuning;
161 dp3m.params.tuning = false;
162
163 if (tune_timings <= 0) {
164 throw std::domain_error("Parameter 'timings' must be > 0");
165 }
166
168 throw std::domain_error("DipolarP3M requires a cubic mesh");
169 }
170}
171
172namespace {
173template <int cao> struct AssignDipole {
174 void operator()(dp3m_data_struct &dp3m, Utils::Vector3d const &real_pos,
175 Utils::Vector3d const &dip) const {
176 auto const weights = p3m_calculate_interpolation_weights<cao>(
177 real_pos, dp3m.params.ai, dp3m.local_mesh);
178 p3m_interpolate<cao>(dp3m.local_mesh, weights,
179 [&dip, &dp3m](int ind, double w) {
180 dp3m.rs_mesh_dip[0][ind] += w * dip[0];
181 dp3m.rs_mesh_dip[1][ind] += w * dip[1];
182 dp3m.rs_mesh_dip[2][ind] += w * dip[2];
183 });
184
185 dp3m.inter_weights.store<cao>(weights);
186 }
187};
188} // namespace
189
192
193 /* prepare local FFT mesh */
194 for (auto &i : dp3m.rs_mesh_dip)
195 for (int j = 0; j < dp3m.local_mesh.size; j++)
196 i[j] = 0.;
197
198 for (auto const &p : particles) {
199 if (p.dipm() != 0.) {
200 Utils::integral_parameter<int, AssignDipole, 1, 7>(dp3m.params.cao, dp3m,
201 p.pos(), p.calc_dip());
202 }
203 }
204}
205
206namespace {
207template <int cao> struct AssignTorques {
208 void operator()(dp3m_data_struct const &dp3m, double prefac, int d_rs,
209 ParticleRange const &particles) const {
210
211 /* magnetic particle index */
212 auto p_index = std::size_t{0ul};
213
214 for (auto &p : particles) {
215 if (p.dipm() != 0.) {
216 auto const w = dp3m.inter_weights.load<cao>(p_index);
217
218 Utils::Vector3d E{};
220 [&E, &dp3m, d_rs](int ind, double w) {
221 E[d_rs] += w * dp3m.rs_mesh[ind];
222 });
223
224 p.torque() -= vector_product(p.calc_dip(), prefac * E);
225 ++p_index;
226 }
227 }
228 }
229};
230
231template <int cao> struct AssignForces {
232 void operator()(dp3m_data_struct const &dp3m, double prefac, int d_rs,
233 ParticleRange const &particles) const {
234
235 /* magnetic particle index */
236 auto p_index = std::size_t{0ul};
237
238 for (auto &p : particles) {
239 if (p.dipm() != 0.) {
240 auto const w = dp3m.inter_weights.load<cao>(p_index);
241
242 Utils::Vector3d E{};
243 p3m_interpolate(dp3m.local_mesh, w, [&E, &dp3m](int ind, double w) {
244 E[0] += w * dp3m.rs_mesh_dip[0][ind];
245 E[1] += w * dp3m.rs_mesh_dip[1][ind];
246 E[2] += w * dp3m.rs_mesh_dip[2][ind];
247 });
248
249 p.force()[d_rs] += p.calc_dip() * prefac * E;
250 ++p_index;
251 }
252 }
253 }
254};
255} // namespace
256
257double DipolarP3M::long_range_kernel(bool force_flag, bool energy_flag,
258 ParticleRange const &particles) {
259 /* k-space energy */
260 double energy = 0.;
261 auto const &system = get_system();
262 auto const &box_geo = *system.box_geo;
263 auto const dipole_prefac = prefactor / Utils::int_pow<3>(dp3m.params.mesh[0]);
264#ifdef NPT
265 auto const npt_flag =
266 force_flag and (system.propagation->integ_switch == INTEG_METHOD_NPT_ISO);
267#else
268 auto constexpr npt_flag = false;
269#endif
270
271 if (dp3m.sum_mu2 > 0.) {
272 dipole_assign(particles);
273 /* Gather information for FFT grid inside the nodes domain (inner local
274 * mesh) and perform forward 3D FFT (Charge Assignment Mesh). */
275 std::array<double *, 3> meshes = {{dp3m.rs_mesh_dip[0].data(),
276 dp3m.rs_mesh_dip[1].data(),
277 dp3m.rs_mesh_dip[2].data()}};
278
281
285 // Note: after these calls, the grids are in the order yzx and not xyz
286 // anymore!!!
287 }
288
289 /* === k-space energy calculation === */
290 if (energy_flag or npt_flag) {
291 /*********************
292 Dipolar energy
293 **********************/
294 if (dp3m.sum_mu2 > 0.) {
295 /* i*k differentiation for dipolar gradients:
296 * |(\Fourier{\vect{mu}}(k)\cdot \vect{k})|^2 */
297 int ind = 0;
298 int i = 0;
299 int j[3];
300 double node_energy = 0.0;
301 for (j[0] = 0; j[0] < dp3m.fft.plan[3].new_mesh[0]; j[0]++) {
302 for (j[1] = 0; j[1] < dp3m.fft.plan[3].new_mesh[1]; j[1]++) {
303 for (j[2] = 0; j[2] < dp3m.fft.plan[3].new_mesh[2]; j[2]++) {
304 node_energy +=
305 dp3m.g_energy[i] *
306 (Utils::sqr(
307 dp3m.rs_mesh_dip[0][ind] *
308 dp3m.d_op[0][j[2] + dp3m.fft.plan[3].start[2]] +
309 dp3m.rs_mesh_dip[1][ind] *
310 dp3m.d_op[0][j[0] + dp3m.fft.plan[3].start[0]] +
311 dp3m.rs_mesh_dip[2][ind] *
312 dp3m.d_op[0][j[1] + dp3m.fft.plan[3].start[1]]) +
314 dp3m.rs_mesh_dip[0][ind + 1] *
315 dp3m.d_op[0][j[2] + dp3m.fft.plan[3].start[2]] +
316 dp3m.rs_mesh_dip[1][ind + 1] *
317 dp3m.d_op[0][j[0] + dp3m.fft.plan[3].start[0]] +
318 dp3m.rs_mesh_dip[2][ind + 1] *
319 dp3m.d_op[0][j[1] + dp3m.fft.plan[3].start[1]]));
320 ind += 2;
321 i++;
322 }
323 }
324 }
325 node_energy *= dipole_prefac * Utils::pi() * box_geo.length_inv()[0];
326 boost::mpi::reduce(comm_cart, node_energy, energy, std::plus<>(), 0);
327
328 if (dp3m.energy_correction == 0.)
329 calc_energy_correction();
330
331 if (this_node == 0) {
332 /* self energy correction */
333 energy -= prefactor * dp3m.sum_mu2 * Utils::sqrt_pi_i() * (2. / 3.) *
334 Utils::int_pow<3>(dp3m.params.alpha);
335
336 /* dipolar energy correction due to systematic Madelung-self effects */
337 energy += prefactor * dp3m.energy_correction / box_geo.volume();
338 }
339 }
340 } // if (energy_flag)
341
342 /* === k-space force calculation === */
343 if (force_flag) {
344 /****************************
345 * DIPOLAR TORQUES (k-space)
346 ****************************/
347 if (dp3m.sum_mu2 > 0.) {
348 auto const two_pi_L_i = 2. * Utils::pi() * box_geo.length_inv()[0];
349 /* fill in ks_mesh array for torque calculation */
350 int ind = 0;
351 int i = 0;
352 int j[3];
353 double tmp0, tmp1;
354
355 for (j[0] = 0; j[0] < dp3m.fft.plan[3].new_mesh[0]; j[0]++) { // j[0]=n_y
356 for (j[1] = 0; j[1] < dp3m.fft.plan[3].new_mesh[1];
357 j[1]++) { // j[1]=n_z
358 for (j[2] = 0; j[2] < dp3m.fft.plan[3].new_mesh[2];
359 j[2]++) { // j[2]=n_x
360 // tmp0 = Re(mu)*k, tmp1 = Im(mu)*k
361
362 tmp0 = dp3m.rs_mesh_dip[0][ind] *
363 dp3m.d_op[0][j[2] + dp3m.fft.plan[3].start[2]] +
364 dp3m.rs_mesh_dip[1][ind] *
365 dp3m.d_op[0][j[0] + dp3m.fft.plan[3].start[0]] +
366 dp3m.rs_mesh_dip[2][ind] *
367 dp3m.d_op[0][j[1] + dp3m.fft.plan[3].start[1]];
368
369 tmp1 = dp3m.rs_mesh_dip[0][ind + 1] *
370 dp3m.d_op[0][j[2] + dp3m.fft.plan[3].start[2]] +
371 dp3m.rs_mesh_dip[1][ind + 1] *
372 dp3m.d_op[0][j[0] + dp3m.fft.plan[3].start[0]] +
373 dp3m.rs_mesh_dip[2][ind + 1] *
374 dp3m.d_op[0][j[1] + dp3m.fft.plan[3].start[1]];
375
376 /* the optimal influence function is the same for torques
377 and energy */
378 dp3m.ks_mesh[ind] = tmp0 * dp3m.g_energy[i];
379 dp3m.ks_mesh[ind + 1] = tmp1 * dp3m.g_energy[i];
380 ind += 2;
381 i++;
382 }
383 }
384 }
385
386 /* Force component loop */
387 for (int d = 0; d < 3; d++) {
388 auto const d_rs = (d + dp3m.ks_pnum) % 3;
389 ind = 0;
390 for (j[0] = 0; j[0] < dp3m.fft.plan[3].new_mesh[0]; j[0]++) {
391 for (j[1] = 0; j[1] < dp3m.fft.plan[3].new_mesh[1]; j[1]++) {
392 for (j[2] = 0; j[2] < dp3m.fft.plan[3].new_mesh[2]; j[2]++) {
393 dp3m.rs_mesh[ind] =
394 dp3m.d_op[0][j[d] + dp3m.fft.plan[3].start[d]] *
395 dp3m.ks_mesh[ind];
396 ind++;
397 dp3m.rs_mesh[ind] =
398 dp3m.d_op[0][j[d] + dp3m.fft.plan[3].start[d]] *
399 dp3m.ks_mesh[ind];
400 ind++;
401 }
402 }
403 }
404
405 /* Back FFT force component mesh */
407 /* redistribute force component mesh */
410 /* Assign force component from mesh to particle */
411 Utils::integral_parameter<int, AssignTorques, 1, 7>(
412 dp3m.params.cao, dp3m, dipole_prefac * two_pi_L_i, d_rs, particles);
413 }
414
415 /***************************
416 DIPOLAR FORCES (k-space)
417 ****************************/
418
419 // Compute forces after torques because the algorithm below overwrites the
420 // grids dp3m.rs_mesh_dip !
421 // Note: I'll do here 9 inverse FFTs. By symmetry, we can reduce this
422 // number to 6 !
423 /* fill in ks_mesh array for force calculation */
424 ind = 0;
425 i = 0;
426 for (j[0] = 0; j[0] < dp3m.fft.plan[3].new_mesh[0]; j[0]++) { // j[0]=n_y
427 for (j[1] = 0; j[1] < dp3m.fft.plan[3].new_mesh[1];
428 j[1]++) { // j[1]=n_z
429 for (j[2] = 0; j[2] < dp3m.fft.plan[3].new_mesh[2];
430 j[2]++) { // j[2]=n_x
431 // tmp0 = Im(mu)*k, tmp1 = -Re(mu)*k
432 tmp0 = dp3m.rs_mesh_dip[0][ind + 1] *
433 dp3m.d_op[0][j[2] + dp3m.fft.plan[3].start[2]] +
434 dp3m.rs_mesh_dip[1][ind + 1] *
435 dp3m.d_op[0][j[0] + dp3m.fft.plan[3].start[0]] +
436 dp3m.rs_mesh_dip[2][ind + 1] *
437 dp3m.d_op[0][j[1] + dp3m.fft.plan[3].start[1]];
438 tmp1 = dp3m.rs_mesh_dip[0][ind] *
439 dp3m.d_op[0][j[2] + dp3m.fft.plan[3].start[2]] +
440 dp3m.rs_mesh_dip[1][ind] *
441 dp3m.d_op[0][j[0] + dp3m.fft.plan[3].start[0]] +
442 dp3m.rs_mesh_dip[2][ind] *
443 dp3m.d_op[0][j[1] + dp3m.fft.plan[3].start[1]];
444 dp3m.ks_mesh[ind] = tmp0 * dp3m.g_force[i];
445 dp3m.ks_mesh[ind + 1] = -tmp1 * dp3m.g_force[i];
446 ind += 2;
447 i++;
448 }
449 }
450 }
451
452 /* Force component loop */
453 for (int d = 0; d < 3; d++) { /* direction in k-space: */
454 auto const d_rs = (d + dp3m.ks_pnum) % 3;
455 ind = 0;
456 for (j[0] = 0; j[0] < dp3m.fft.plan[3].new_mesh[0];
457 j[0]++) { // j[0]=n_y
458 for (j[1] = 0; j[1] < dp3m.fft.plan[3].new_mesh[1];
459 j[1]++) { // j[1]=n_z
460 for (j[2] = 0; j[2] < dp3m.fft.plan[3].new_mesh[2];
461 j[2]++) { // j[2]=n_x
462 tmp0 = dp3m.d_op[0][j[d] + dp3m.fft.plan[3].start[d]] *
463 dp3m.ks_mesh[ind];
464 dp3m.rs_mesh_dip[0][ind] =
465 dp3m.d_op[0][j[2] + dp3m.fft.plan[3].start[2]] * tmp0;
466 dp3m.rs_mesh_dip[1][ind] =
467 dp3m.d_op[0][j[0] + dp3m.fft.plan[3].start[0]] * tmp0;
468 dp3m.rs_mesh_dip[2][ind] =
469 dp3m.d_op[0][j[1] + dp3m.fft.plan[3].start[1]] * tmp0;
470 ind++;
471 tmp0 = dp3m.d_op[0][j[d] + dp3m.fft.plan[3].start[d]] *
472 dp3m.ks_mesh[ind];
473 dp3m.rs_mesh_dip[0][ind] =
474 dp3m.d_op[0][j[2] + dp3m.fft.plan[3].start[2]] * tmp0;
475 dp3m.rs_mesh_dip[1][ind] =
476 dp3m.d_op[0][j[0] + dp3m.fft.plan[3].start[0]] * tmp0;
477 dp3m.rs_mesh_dip[2][ind] =
478 dp3m.d_op[0][j[1] + dp3m.fft.plan[3].start[1]] * tmp0;
479 ind++;
480 }
481 }
482 }
483 /* Back FFT force component mesh */
484 fft_perform_back(dp3m.rs_mesh_dip[0].data(), false, dp3m.fft,
485 comm_cart);
486 fft_perform_back(dp3m.rs_mesh_dip[1].data(), false, dp3m.fft,
487 comm_cart);
488 fft_perform_back(dp3m.rs_mesh_dip[2].data(), false, dp3m.fft,
489 comm_cart);
490 /* redistribute force component mesh */
491 std::array<double *, 3> meshes = {{dp3m.rs_mesh_dip[0].data(),
492 dp3m.rs_mesh_dip[1].data(),
493 dp3m.rs_mesh_dip[2].data()}};
494
497 /* Assign force component from mesh to particle */
498 Utils::integral_parameter<int, AssignForces, 1, 7>(
499 dp3m.params.cao, dp3m, dipole_prefac * Utils::sqr(two_pi_L_i), d_rs,
500 particles);
501 }
502 } /* if (dp3m.sum_mu2 > 0) */
503 } /* if (force_flag) */
504
506 auto const surface_term =
507 calc_surface_term(force_flag, energy_flag or npt_flag, particles);
508 if (this_node == 0) {
509 energy += surface_term;
510 }
511 }
512 if (npt_flag) {
514 fprintf(stderr, "dipolar_P3M at this moment is added to p_vir[0]\n");
515 }
516 if (not energy_flag) {
517 energy = 0.;
518 }
519
520 return energy;
521}
522
523double DipolarP3M::calc_surface_term(bool force_flag, bool energy_flag,
524 ParticleRange const &particles) {
525 auto const &box_geo = *get_system().box_geo;
526 auto const pref = prefactor * 4. * Utils::pi() / box_geo.volume() /
527 (2. * dp3m.params.epsilon + 1.);
528 auto const n_local_part = particles.size();
529
530 // We put all the dipolar momenta in a the arrays mx,my,mz according to the
531 // id-number of the particles
532 std::vector<double> mx(n_local_part);
533 std::vector<double> my(n_local_part);
534 std::vector<double> mz(n_local_part);
535
536 std::size_t ip = 0u;
537 for (auto const &p : particles) {
538 auto const dip = p.calc_dip();
539 mx[ip] = dip[0];
540 my[ip] = dip[1];
541 mz[ip] = dip[2];
542 ip++;
543 }
544
545 // we will need the sum of all dipolar momenta vectors
546 auto local_dip = Utils::Vector3d{};
547 for (std::size_t i = 0u; i < n_local_part; i++) {
548 local_dip[0] += mx[i];
549 local_dip[1] += my[i];
550 local_dip[2] += mz[i];
551 }
552 auto const box_dip =
553 boost::mpi::all_reduce(comm_cart, local_dip, std::plus<>());
554
555 double energy = 0.;
556 if (energy_flag) {
557 double sum_e = 0.;
558 for (std::size_t i = 0u; i < n_local_part; i++) {
559 sum_e += mx[i] * box_dip[0] + my[i] * box_dip[1] + mz[i] * box_dip[2];
560 }
561 energy =
562 0.5 * pref * boost::mpi::all_reduce(comm_cart, sum_e, std::plus<>());
563 }
564
565 if (force_flag) {
566
567 std::vector<double> sumix(n_local_part);
568 std::vector<double> sumiy(n_local_part);
569 std::vector<double> sumiz(n_local_part);
570
571 for (std::size_t i = 0u; i < n_local_part; i++) {
572 sumix[i] = my[i] * box_dip[2] - mz[i] * box_dip[1];
573 sumiy[i] = mz[i] * box_dip[0] - mx[i] * box_dip[2];
574 sumiz[i] = mx[i] * box_dip[1] - my[i] * box_dip[0];
575 }
576
577 ip = 0u;
578 for (auto &p : particles) {
579 auto &torque = p.torque();
580 torque[0] -= pref * sumix[ip];
581 torque[1] -= pref * sumiy[ip];
582 torque[2] -= pref * sumiz[ip];
583 ip++;
584 }
585 }
586
587 return energy;
588}
589
590void DipolarP3M::calc_influence_function_force() {
591 auto const start = Utils::Vector3i{dp3m.fft.plan[3].start};
592 auto const size = Utils::Vector3i{dp3m.fft.plan[3].new_mesh};
593
594 dp3m.g_force = grid_influence_function<3>(dp3m.params, start, start + size,
595 get_system().box_geo->length());
596}
597
598void DipolarP3M::calc_influence_function_energy() {
599 auto const start = Utils::Vector3i{dp3m.fft.plan[3].start};
600 auto const size = Utils::Vector3i{dp3m.fft.plan[3].new_mesh};
601
602 dp3m.g_energy = grid_influence_function<2>(dp3m.params, start, start + size,
603 get_system().box_geo->length());
604}
605
607 dp3m_data_struct &dp3m;
608 int m_mesh_max = -1, m_mesh_min = -1;
609
610public:
612 double prefactor, int timings)
613 : TuningAlgorithm(system, prefactor, timings), dp3m{input_dp3m} {}
614
615 P3MParameters &get_params() override { return dp3m.params; }
616
617 void on_solver_change() const override { m_system.on_dipoles_change(); }
618
619 std::optional<std::string>
620 layer_correction_veto_r_cut(double) const override {
621 return {};
622 }
623
624 void setup_logger(bool verbose) override {
625 auto const &box_geo = *m_system.box_geo;
626 m_logger = std::make_unique<TuningLogger>(
627 verbose and this_node == 0, "DipolarP3M", TuningLogger::Mode::Dipolar);
628 m_logger->tuning_goals(dp3m.params.accuracy, m_prefactor,
629 box_geo.length()[0], dp3m.sum_dip_part,
630 dp3m.sum_mu2);
631 m_logger->log_tuning_start();
632 }
633
634 std::tuple<double, double, double, double>
636 double r_cut_iL) const override {
637
638 double alpha_L, rs_err, ks_err;
639 auto const &box_geo = *m_system.box_geo;
640
641 /* calc maximal real space error for setting */
642 rs_err = dp3m_real_space_error(box_geo.length()[0], r_cut_iL,
643 dp3m.sum_dip_part, dp3m.sum_mu2, 0.001);
644 // alpha cannot be zero for dipoles because real-space formula breaks down
645
646 if (Utils::sqrt_2() * rs_err > dp3m.params.accuracy) {
647 /* assume rs_err = ks_err -> rs_err = accuracy/sqrt(2.0) -> alpha_L */
648 alpha_L = dp3m_rtbisection(
649 box_geo.length()[0], r_cut_iL, dp3m.sum_dip_part, dp3m.sum_mu2,
650 0.0001 * box_geo.length()[0], 5. * box_geo.length()[0], 0.0001,
651 dp3m.params.accuracy);
652 } else {
653 /* even alpha=0 is ok, however, we cannot choose it since it kills the
654 k-space error formula.
655 Anyways, this very likely NOT the optimal solution */
656 alpha_L = 0.1;
657 }
658
659 /* calculate real-space and k-space error for this alpha_L */
660 rs_err = dp3m_real_space_error(box_geo.length()[0], r_cut_iL,
661 dp3m.sum_dip_part, dp3m.sum_mu2, alpha_L);
662 ks_err = dp3m_k_space_error(box_geo.length()[0], mesh[0], cao,
663 dp3m.sum_dip_part, dp3m.sum_mu2, alpha_L);
664
665 return {Utils::Vector2d{rs_err, ks_err}.norm(), rs_err, ks_err, alpha_L};
666 }
667
668 void determine_mesh_limits() override {
669 if (dp3m.params.mesh[0] == -1) {
670 /* simple heuristic to limit the tried meshes if the accuracy cannot
671 be obtained with smaller meshes, but normally not all these
672 meshes have to be tested */
673 auto const expo = std::log(std::cbrt(dp3m.sum_dip_part)) / std::log(2.);
674 /* Medium-educated guess for the minimal mesh */
675 m_mesh_min = static_cast<int>(std::round(std::pow(2., std::floor(expo))));
676 /* avoid using more than 1 GB of FFT arrays */
677 m_mesh_max = 128;
678 } else {
679 m_mesh_min = m_mesh_max = dp3m.params.mesh[0];
680 m_logger->report_fixed_mesh(dp3m.params.mesh);
681 }
682 }
683
685 auto tuned_params = TuningAlgorithm::Parameters{};
686 auto time_best = time_sentinel;
687 for (auto tmp_mesh = m_mesh_min; tmp_mesh <= m_mesh_max; tmp_mesh += 2) {
688 auto trial_params = TuningAlgorithm::Parameters{};
689 trial_params.mesh = Utils::Vector3i::broadcast(tmp_mesh);
690 trial_params.cao = cao_best;
691
692 auto const trial_time =
693 get_m_time(trial_params.mesh, trial_params.cao, trial_params.r_cut_iL,
694 trial_params.alpha_L, trial_params.accuracy);
695
696 /* this mesh does not work at all */
697 if (trial_time < 0.)
698 continue;
699
700 /* the optimum r_cut for this mesh is the upper limit for higher meshes,
701 everything else is slower */
702 m_r_cut_iL_max = trial_params.r_cut_iL;
703
704 if (trial_time < time_best) {
705 /* new optimum */
707 tuned_params = trial_params;
708 time_best = tuned_params.time = trial_time;
709 } else if (trial_time > time_best + time_granularity or
711 /* no hope of further optimisation */
712 break;
713 }
714 }
715 return tuned_params;
716 }
717};
718
720 auto &system = get_system();
721 auto const &box_geo = *system.box_geo;
722 if (dp3m.params.alpha_L == 0. and dp3m.params.alpha != 0.) {
723 dp3m.params.alpha_L = dp3m.params.alpha * box_geo.length()[0];
724 }
725 if (dp3m.params.r_cut_iL == 0. and dp3m.params.r_cut != 0.) {
726 dp3m.params.r_cut_iL = dp3m.params.r_cut * box_geo.length_inv()[0];
727 }
728 if (not is_tuned()) {
730 if (dp3m.sum_dip_part == 0) {
731 throw std::runtime_error(
732 "DipolarP3M: no dipolar particles in the system");
733 }
734 try {
736 parameters.setup_logger(tune_verbose);
737 // parameter ranges
738 parameters.determine_mesh_limits();
739 parameters.determine_r_cut_limits();
740 parameters.determine_cao_limits(3);
741 // run tuning algorithm
742 parameters.tune();
743 m_is_tuned = true;
744 system.on_dipoles_change();
745 } catch (...) {
746 dp3m.params.tuning = false;
747 throw;
748 }
749 }
750 init();
751}
752
753/** Tuning dipolar-P3M */
754static auto dp3m_tune_aliasing_sums(int nx, int ny, int nz, int mesh,
755 double mesh_i, int cao, double alpha_L_i) {
756 using Utils::sinc;
757
758 auto const factor1 = Utils::sqr(Utils::pi() * alpha_L_i);
759
760 auto alias1 = 0.;
761 auto alias2 = 0.;
762 for (int mx = -P3M_BRILLOUIN; mx <= P3M_BRILLOUIN; mx++) {
763 auto const nmx = nx + mx * mesh;
764 auto const fnmx = mesh_i * nmx;
765 for (int my = -P3M_BRILLOUIN; my <= P3M_BRILLOUIN; my++) {
766 auto const nmy = ny + my * mesh;
767 auto const fnmy = mesh_i * nmy;
768 for (int mz = -P3M_BRILLOUIN; mz <= P3M_BRILLOUIN; mz++) {
769 auto const nmz = nz + mz * mesh;
770 auto const fnmz = mesh_i * nmz;
771
772 auto const nm2 = Utils::sqr(nmx) + Utils::sqr(nmy) + Utils::sqr(nmz);
773 auto const ex = std::exp(-factor1 * nm2);
774
775 auto const U2 = pow(sinc(fnmx) * sinc(fnmy) * sinc(fnmz), 2. * cao);
776
777 alias1 += Utils::sqr(ex) * nm2;
778 alias2 += U2 * ex * pow((nx * nmx + ny * nmy + nz * nmz), 3.) / nm2;
779 }
780 }
781 }
782 return std::make_pair(alias1, alias2);
783}
784
785/** Calculate the k-space error of dipolar-P3M */
786static double dp3m_k_space_error(double box_size, int mesh, int cao,
787 int n_c_part, double sum_q2, double alpha_L) {
788 double he_q = 0.;
789 auto const mesh_i = 1. / mesh;
790 auto const alpha_L_i = 1. / alpha_L;
791
792 for (int nx = -mesh / 2; nx < mesh / 2; nx++)
793 for (int ny = -mesh / 2; ny < mesh / 2; ny++)
794 for (int nz = -mesh / 2; nz < mesh / 2; nz++)
795 if ((nx != 0) || (ny != 0) || (nz != 0)) {
796 auto const n2 = Utils::sqr(nx) + Utils::sqr(ny) + Utils::sqr(nz);
797 auto const cs = p3m_analytic_cotangent_sum(nx, mesh_i, cao) *
798 p3m_analytic_cotangent_sum(ny, mesh_i, cao) *
799 p3m_analytic_cotangent_sum(nz, mesh_i, cao);
800 auto const [alias1, alias2] =
801 dp3m_tune_aliasing_sums(nx, ny, nz, mesh, mesh_i, cao, alpha_L_i);
802 auto const d =
803 alias1 - Utils::sqr(alias2 / cs) /
804 Utils::int_pow<3>(static_cast<double>(n2));
805 /* at high precision, d can become negative due to extinction;
806 also, don't take values that have no significant digits left*/
807 if (d > 0 && (fabs(d / alias1) > ROUND_ERROR_PREC))
808 he_q += d;
809 }
810
811 return 8. * Utils::sqr(Utils::pi()) / 3. * sum_q2 * sqrt(he_q / n_c_part) /
812 Utils::int_pow<4>(box_size);
813}
814
815/** Calculate the value of the errors for the REAL part of the force in terms
816 * of the splitting parameter alpha of Ewald. Based on eq. (33) @cite wang01a.
817 *
818 * Please note that in this more refined approach we don't use
819 * eq. (37), but eq. (33) which maintains all the powers in alpha.
820 */
821static double dp3m_real_space_error(double box_size, double r_cut_iL,
822 int n_c_part, double sum_q2,
823 double alpha_L) {
824 double d_error_f, d_cc, d_dc, d_con;
825
826 auto const d_rcut = r_cut_iL * box_size;
827 auto const d_rcut2 = Utils::sqr(d_rcut);
828 auto const d_rcut4 = Utils::sqr(d_rcut2);
829
830 auto const d_a2 = Utils::sqr(alpha_L) / Utils::sqr(box_size);
831
832 auto const d_c = sum_q2 * exp(-d_a2 * d_rcut2);
833
834 d_cc = 4. * Utils::sqr(d_a2) * Utils::sqr(d_rcut2) + 6. * d_a2 * d_rcut2 + 3.;
835
836 d_dc = 8. * Utils::int_pow<3>(d_a2) * Utils::int_pow<3>(d_rcut2) +
837 20. * Utils::sqr(d_a2) * d_rcut4 + 30. * d_a2 * d_rcut2 + 15.;
838
839 d_con = 1. / sqrt(Utils::int_pow<3>(box_size) * Utils::sqr(d_a2) * d_rcut *
840 Utils::sqr(d_rcut4) * static_cast<double>(n_c_part));
841
842 d_error_f = d_c * d_con *
843 sqrt((13. / 6.) * Utils::sqr(d_cc) +
844 (2. / 15.) * Utils::sqr(d_dc) - (13. / 15.) * d_cc * d_dc);
845
846 return d_error_f;
847}
848
849/** Using bisection, find the root of a function "func-tuned_accuracy/sqrt(2.)"
850 * known to lie between x1 and x2. The root, returned as rtbis, will be
851 * refined until its accuracy is \f$\pm\f$ @p xacc.
852 */
853double dp3m_rtbisection(double box_size, double r_cut_iL, int n_c_part,
854 double sum_q2, double x1, double x2, double xacc,
855 double tuned_accuracy) {
856 constexpr int JJ_RTBIS_MAX = 40;
857
858 auto const constant = tuned_accuracy / Utils::sqrt_2();
859
860 auto const f1 =
861 dp3m_real_space_error(box_size, r_cut_iL, n_c_part, sum_q2, x1) -
862 constant;
863 auto const f2 =
864 dp3m_real_space_error(box_size, r_cut_iL, n_c_part, sum_q2, x2) -
865 constant;
866 if (f1 * f2 >= 0.0) {
867 throw std::runtime_error(
868 "Root must be bracketed for bisection in dp3m_rtbisection");
869 }
870 // Orient the search dx, and set rtb to x1 or x2 ...
871 double dx;
872 double rtb = f1 < 0.0 ? (dx = x2 - x1, x1) : (dx = x1 - x2, x2);
873 for (int j = 1; j <= JJ_RTBIS_MAX; j++) {
874 auto const xmid = rtb + (dx *= 0.5);
875 auto const fmid =
876 dp3m_real_space_error(box_size, r_cut_iL, n_c_part, sum_q2, xmid) -
877 constant;
878 if (fmid <= 0.0)
879 rtb = xmid;
880 if (fabs(dx) < xacc || fmid == 0.0)
881 return rtb;
882 }
883 throw std::runtime_error("Too many bisections in dp3m_rtbisection");
884}
885
886void DipolarP3M::sanity_checks_boxl() const {
887 auto const &system = get_system();
888 auto const &box_geo = *system.box_geo;
889 auto const &local_geo = *system.local_geo;
890 for (unsigned int i = 0u; i < 3u; i++) {
891 /* check k-space cutoff */
892 if (dp3m.params.cao_cut[i] >= box_geo.length_half()[i]) {
893 std::stringstream msg;
894 msg << "dipolar P3M_init: k-space cutoff " << dp3m.params.cao_cut[i]
895 << " is larger than half of box dimension " << box_geo.length()[i];
896 throw std::runtime_error(msg.str());
897 }
898 if (dp3m.params.cao_cut[i] >= local_geo.length()[i]) {
899 std::stringstream msg;
900 msg << "dipolar P3M_init: k-space cutoff " << dp3m.params.cao_cut[i]
901 << " is larger than local box dimension " << local_geo.length()[i];
902 throw std::runtime_error(msg.str());
903 }
904 }
905
906 if ((box_geo.length()[0] != box_geo.length()[1]) or
907 (box_geo.length()[1] != box_geo.length()[2])) {
908 throw std::runtime_error("DipolarP3M: requires a cubic box");
909 }
910}
911
912void DipolarP3M::sanity_checks_periodicity() const {
913 auto const &box_geo = *get_system().box_geo;
914 if (!box_geo.periodic(0) or !box_geo.periodic(1) or !box_geo.periodic(2)) {
915 throw std::runtime_error(
916 "DipolarP3M: requires periodicity (True, True, True)");
917 }
918}
919
920void DipolarP3M::sanity_checks_cell_structure() const {
921 auto const &local_geo = *get_system().local_geo;
922 if (local_geo.cell_structure_type() != CellStructureType::REGULAR and
923 local_geo.cell_structure_type() != CellStructureType::HYBRID) {
924 throw std::runtime_error(
925 "DipolarP3M: requires the regular or hybrid decomposition cell system");
926 }
927 if (::communicator.size > 1 and
928 local_geo.cell_structure_type() == CellStructureType::HYBRID) {
929 throw std::runtime_error(
930 "DipolarP3M: does not work with the hybrid decomposition cell system, "
931 "if using more than one MPI node");
932 }
933}
934
935void DipolarP3M::sanity_checks_node_grid() const {
936 auto const &node_grid = ::communicator.node_grid;
937 if (node_grid[0] < node_grid[1] or node_grid[1] < node_grid[2]) {
938 throw std::runtime_error(
939 "DipolarP3M: node grid must be sorted, largest first");
940 }
941}
942
943void DipolarP3M::scaleby_box_l() {
944 auto const &box_geo = *get_system().box_geo;
945 dp3m.params.r_cut = dp3m.params.r_cut_iL * box_geo.length()[0];
946 dp3m.params.alpha = dp3m.params.alpha_L * box_geo.length_inv()[0];
947 dp3m.params.recalc_a_ai_cao_cut(box_geo.length());
949 sanity_checks_boxl();
950 calc_influence_function_force();
951 calc_influence_function_energy();
953}
954
955void DipolarP3M::calc_energy_correction() {
956 auto const &box_geo = *get_system().box_geo;
957 auto const Ukp3m = calc_average_self_energy_k_space() * box_geo.volume();
958 auto const Ewald_volume = Utils::int_pow<3>(dp3m.params.alpha_L);
959 auto const Eself = -2. * Ewald_volume * Utils::sqrt_pi_i() / 3.;
961 -dp3m.sum_mu2 * (Ukp3m + Eself + 2. * Utils::pi() / 3.);
962}
963
964#ifdef NPT
968#endif // NPT
969#endif // DP3M
@ HYBRID
Hybrid decomposition.
@ REGULAR
Regular decomposition.
@ INTEG_METHOD_NPT_ISO
Vector implementation and trait types for boost qvm interoperability.
__global__ float float * torque
float u[3]
std::optional< std::string > layer_correction_veto_r_cut(double) const override
Definition dp3m.cpp:620
TuningAlgorithm::Parameters get_time() override
Definition dp3m.cpp:684
void setup_logger(bool verbose) override
Definition dp3m.cpp:624
std::tuple< double, double, double, double > calculate_accuracy(Utils::Vector3i const &mesh, int cao, double r_cut_iL) const override
Definition dp3m.cpp:635
P3MParameters & get_params() override
Definition dp3m.cpp:615
DipolarTuningAlgorithm(System::System &system, dp3m_data_struct &input_dp3m, double prefactor, int timings)
Definition dp3m.cpp:611
void determine_mesh_limits() override
Definition dp3m.cpp:668
void on_solver_change() const override
Definition dp3m.cpp:617
void set_prefactor(double new_prefactor)
double prefactor
Magnetostatics prefactor.
A range of particles.
base_type::size_type size() const
Main system class.
std::shared_ptr< BoxGeometry > box_geo
Tuning algorithm for P3M.
double get_m_time(Utils::Vector3i const &mesh, int &tuned_cao, double &tuned_r_cut_iL, double &tuned_alpha_L, double &tuned_accuracy)
Get the optimal alpha and the corresponding computation time for a fixed mesh.
static auto constexpr time_sentinel
Value for invalid time measurements.
static auto constexpr max_n_consecutive_trials
Maximal number of consecutive trials that don't improve runtime.
System::System & m_system
void determine_cao_limits(int initial_cao)
Determine a sensible range for the charge assignment order.
void determine_r_cut_limits()
Determine a sensible range for the real-space cutoff.
std::unique_ptr< TuningLogger > m_logger
static auto constexpr time_granularity
Granularity of the time measurement (milliseconds).
static Vector< T, N > broadcast(T const &s)
Create a vector that has all entries set to one value.
Definition Vector.hpp:110
T norm() const
Definition Vector.hpp:131
void store(const InterpolationWeights< cao > &w)
Push back weights for one point.
InterpolationWeights< cao > load(std::size_t i) const
Load entry from the cache.
void reset(int cao)
Reset the cache.
void spread_grid(Utils::Span< double * > meshes, const boost::mpi::communicator &comm, const Utils::Vector3i &dim)
void resize(const boost::mpi::communicator &comm, const P3MLocalMesh &local_mesh)
Definition send_mesh.cpp:73
void gather_grid(Utils::Span< double * > meshes, const boost::mpi::communicator &comm, const Utils::Vector3i &dim)
Common functions for dipolar and charge P3M.
auto constexpr P3M_EPSILON_METALLIC
This value indicates metallic boundary conditions.
Definition common.hpp:45
Communicator communicator
boost::mpi::communicator comm_cart
The communicator.
int this_node
The number of this node.
This file contains the defaults for ESPResSo.
#define ROUND_ERROR_PREC
Precision for capture of round off errors.
Definition config.hpp:66
#define P3M_BRILLOUIN
P3M: Number of Brillouin zones taken into account in the calculation of the optimal influence functio...
Definition config.hpp:53
__device__ void vector_product(float const *a, float const *b, float *out)
static double dp3m_k_space_error(double box_size, int mesh, int cao, int n_c_part, double sum_q2, double alpha_L)
Calculate the k-space error of dipolar-P3M.
Definition dp3m.cpp:786
void npt_add_virial_magnetic_contribution(double energy)
Update the NpT virial.
Definition dp3m.cpp:965
double dp3m_rtbisection(double box_size, double r_cut_iL, int n_c_part, double sum_q2, double x1, double x2, double xacc, double tuned_accuracy)
Compute the value of alpha through a bisection method.
Definition dp3m.cpp:853
static auto dp3m_tune_aliasing_sums(int nx, int ny, int nz, int mesh, double mesh_i, int cao, double alpha_L_i)
Tuning dipolar-P3M.
Definition dp3m.cpp:754
static double dp3m_real_space_error(double box_size, double r_cut_iL, int n_c_part, double sum_q2, double alpha_L)
Calculate the value of the errors for the REAL part of the force in terms of the splitting parameter ...
Definition dp3m.cpp:821
P3M algorithm for long-range magnetic dipole-dipole interaction.
This file contains the errorhandling code for severe errors, like a broken bond or illegal parameter ...
void fft_perform_forw(double *data, fft_data_struct &fft, const boost::mpi::communicator &comm)
Perform an in-place forward 3D FFT.
Definition fft.cpp:683
void fft_perform_back(double *data, bool check_complex, fft_data_struct &fft, const boost::mpi::communicator &comm)
Perform an in-place backward 3D FFT.
Definition fft.cpp:714
int fft_init(Utils::Vector3i const &ca_mesh_dim, int const *ca_mesh_margin, Utils::Vector3i const &global_mesh_dim, Utils::Vector3d const &global_mesh_off, int &ks_pnum, fft_data_struct &fft, Utils::Vector3i const &grid, boost::mpi::communicator const &comm)
Initialize everything connected to the 3D-FFT.
Definition fft.cpp:484
Routines, row decomposition, data structures and communication for the 3D-FFT.
double grid_influence_function_self_energy(P3MParameters const &params, Utils::Vector3i const &n_start, Utils::Vector3i const &n_end, std::vector< double > const &g)
Calculate self-energy of the influence function.
void p3m_interpolate(P3MLocalMesh const &local_mesh, InterpolationWeights< cao > const &weights, Kernel kernel)
P3M grid interpolation.
ParticleRange particles(Utils::Span< Cell *const > cells)
DEVICE_QUALIFIER constexpr T pi()
Ratio of diameter and circumference of a circle.
Definition constants.hpp:36
DEVICE_QUALIFIER T sinc(T d)
Calculates the sinc-function as sin(PI*x)/(PI*x).
Definition sinc.hpp:45
DEVICE_QUALIFIER constexpr T sqr(T x)
Calculates the SQuaRe of x.
Definition sqr.hpp:26
DEVICE_QUALIFIER constexpr Span< T > make_span(T *p, std::size_t N)
Definition Span.hpp:112
DEVICE_QUALIFIER constexpr T sqrt_2()
Square root of 2.
Definition constants.hpp:64
DEVICE_QUALIFIER constexpr T sqrt_pi_i()
One over square root of pi.
Definition constants.hpp:43
void npt_add_virial_contribution(double energy)
Definition npt.cpp:141
Exports for the NpT code.
static __device__ double p3m_analytic_cotangent_sum(int n, double mesh_i)
Utils::Vector3i node_grid
void init()
Recalculate all derived parameters.
Definition dp3m.cpp:119
int tune_timings
Magnetostatics prefactor.
Definition dp3m.hpp:100
void dipole_assign(ParticleRange const &particles)
Assign the physical dipoles using the tabulated assignment function.
Definition dp3m.cpp:190
dp3m_data_struct dp3m
Dipolar P3M parameters.
Definition dp3m.hpp:97
DipolarP3M(P3MParameters &&parameters, double prefactor, int tune_timings, bool tune_verbose)
Definition dp3m.cpp:154
double long_range_kernel(bool force_flag, bool energy_flag, ParticleRange const &particles)
Compute the k-space part of forces and energies.
Definition dp3m.cpp:257
void count_magnetic_particles()
Count the number of magnetic particles and calculate the sum of the squared dipole moments.
Definition dp3m.cpp:76
void tune()
Tune dipolar P3M parameters to desired accuracy.
Definition dp3m.cpp:719
bool is_tuned() const
Definition dp3m.hpp:170
bool tune_verbose
Definition dp3m.hpp:101
Utils::Vector3i dim
dimension (size) of local mesh.
Definition common.hpp:186
int size
number of local mesh points.
Definition common.hpp:188
void recalc_ld_pos(P3MParameters const &params)
Recalculate quantities derived from the mesh and box length: ld_pos (position of the left down mesh).
Definition common.hpp:213
void calc_local_ca_mesh(P3MParameters const &params, LocalBox const &local_geo, double skin, double space_layer)
Calculate properties of the local FFT mesh for the charge assignment process.
Definition common.cpp:78
int margin[6]
number of margin mesh points.
Definition common.hpp:201
Structure to hold P3M parameters and some dependent variables.
Definition common.hpp:67
Utils::Vector3d cao_cut
cutoff for charge assignment.
Definition common.hpp:89
double alpha
unscaled alpha_L for use with fast inline functions only
Definition common.hpp:96
double r_cut_iL
cutoff radius for real space electrostatics (>0), rescaled to r_cut_iL = r_cut * box_l_i.
Definition common.hpp:75
int cao
charge assignment order ([0,7]).
Definition common.hpp:82
double accuracy
accuracy of the actual parameter set.
Definition common.hpp:84
double alpha_L
Ewald splitting parameter (0.
Definition common.hpp:72
int cao3
number of points unto which a single charge is interpolated, i.e.
Definition common.hpp:102
Utils::Vector3d mesh_off
offset of the first mesh point (lower left corner) from the coordinate origin ([0,...
Definition common.hpp:80
Utils::Vector3d ai
inverse mesh constant.
Definition common.hpp:93
double r_cut
unscaled r_cut_iL for use with fast inline functions only
Definition common.hpp:99
void recalc_a_ai_cao_cut(Utils::Vector3d const &box_l)
Recalculate quantities derived from the mesh and box length: a, ai and cao_cut.
Definition common.hpp:167
bool tuning
tuning or production?
Definition common.hpp:69
Utils::Vector3i mesh
number of mesh points per coordinate direction (>0).
Definition common.hpp:77
double epsilon
epsilon of the "surrounding dielectric".
Definition common.hpp:87
void operator()(dp3m_data_struct &dp3m, Utils::Vector3d const &real_pos, Utils::Vector3d const &dip) const
Definition dp3m.cpp:174
void operator()(dp3m_data_struct const &dp3m, double prefac, int d_rs, ParticleRange const &particles) const
Definition dp3m.cpp:232
void operator()(dp3m_data_struct const &dp3m, double prefac, int d_rs, ParticleRange const &particles) const
Definition dp3m.cpp:208
p3m_send_mesh sm
send/recv mesh sizes
Definition dp3m.hpp:86
P3MLocalMesh local_mesh
local mesh.
Definition dp3m.hpp:67
fft_data_struct fft
Definition dp3m.hpp:91
fft_vector< double > rs_mesh
real space mesh (local) for CA/FFT.
Definition dp3m.hpp:69
double sum_mu2
Sum of square of magnetic dipoles (only on head node).
Definition dp3m.hpp:78
std::array< fft_vector< double >, 3 > rs_mesh_dip
real space mesh (local) for CA/FFT of the dipolar field.
Definition dp3m.hpp:71
int sum_dip_part
number of dipolar particles (only on head node).
Definition dp3m.hpp:76
p3m_interpolation_cache inter_weights
Definition dp3m.hpp:83
std::vector< double > ks_mesh
k-space mesh (local) for k-space calculation and FFT.
Definition dp3m.hpp:73
double energy_correction
cached k-space self-energy correction
Definition dp3m.hpp:89
fft_forw_plan plan[4]
Information for forward FFTs.
Definition fft.hpp:154
int start[3]
lower left point of local FFT mesh in global FFT mesh coordinates.
Definition fft.hpp:111
int new_mesh[3]
size of local mesh after communication, also used for actual FFT.
Definition fft.hpp:109
std::vector< double > g_energy
Energy optimised influence function (k-space)
P3MParameters params
void calc_differential_operator()
Calculate the Fourier transformed differential operator.
std::array< std::vector< int >, 3 > d_op
Spatial differential operator in k-space.
std::vector< double > g_force
Force optimised influence function (k-space)
int ks_pnum
number of permutations in k_space