ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
dlc.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#include "config/config.hpp"
23
24#ifdef DIPOLES
25
27
31#include "p3m/common.hpp"
32
33#include "BoxGeometry.hpp"
34#include "Particle.hpp"
36#include "communication.hpp"
37#include "errorhandling.hpp"
38#include "system/System.hpp"
39
40#include <utils/math/sqr.hpp>
41
42#include <boost/mpi/collectives/all_reduce.hpp>
43#include <boost/mpi/collectives/reduce.hpp>
44#include <boost/mpi/operations.hpp>
45
46#include <mpi.h>
47
48#include <algorithm>
49#include <cassert>
50#include <cmath>
51#include <cstdio>
52#include <functional>
53#include <limits>
54#include <numbers>
55#include <numeric>
56#include <stdexcept>
57#include <variant>
58#include <vector>
59
60void DipolarLayerCorrection::check_gap(Particle const &p) const {
61 if (p.dipm() != 0.) {
62 auto const z = p.pos()[2];
63 if (z < 0. or z > dlc.box_h) {
64 runtimeErrorMsg() << "Particle " << p.id() << " entered DLC gap region "
65 << "by " << z - ((z < 0.) ? 0. : dlc.box_h);
66 }
67 }
68}
69
70/** Calculate the maximal dipole moment in the system */
71static double calc_mu_max(System::System const &system) {
72 auto const local_particles = system.cell_structure->local_particles();
73 auto const mu_max_local = std::accumulate(
74 local_particles.begin(), local_particles.end(), 0.,
75 [](double mu, Particle const &p) { return std::max(mu, p.dipm()); });
76
77 return boost::mpi::all_reduce(comm_cart, mu_max_local,
78 boost::mpi::maximum<double>());
79}
80
81static double g1_DLC_dip(double g, double x) {
82 auto const c = g / x;
83 auto const cc2 = c * c;
84 auto const x3 = x * x * x;
85 auto const a = g * g * g / x + 1.5 * cc2 + 1.5 * g / x3 + 0.75 / (x3 * x);
86 return a;
87}
88
89static double g2_DLC_dip(double g, double x) {
90 auto const x2 = x * x;
91 auto const a = g * g / x + 2.0 * g / x2 + 2.0 / (x2 * x);
92 return a;
93}
94
95/** Compute the box magnetic dipole. */
97
98 Utils::Vector3d local_dip{};
99 for (auto const &p : particles) {
100 if (p.dipm() != 0.) {
101 local_dip += p.calc_dip();
102 }
103 }
104
105 return boost::mpi::all_reduce(comm_cart, local_dip, std::plus<>());
106}
107
108/**
109 * @brief Compute the dipolar force and torque corrections.
110 * Algorithm implemented accordingly to @cite brodka04a.
111 */
112static void dipolar_force_corrections(int kcut,
113 std::vector<Utils::Vector3d> &fs,
114 std::vector<Utils::Vector3d> &ts,
115 ParticleRange const &particles,
116 BoxGeometry const &box_geo) {
117 auto const facux = 2. * std::numbers::pi * box_geo.length_inv()[0];
118 auto const facuy = 2. * std::numbers::pi * box_geo.length_inv()[1];
119
120 auto const n_local_particles = particles.size();
121 std::vector<double> ReSjp(n_local_particles);
122 std::vector<double> ReSjm(n_local_particles);
123 std::vector<double> ImSjp(n_local_particles);
124 std::vector<double> ImSjm(n_local_particles);
125 std::vector<double> ReGrad_Mup(n_local_particles);
126 std::vector<double> ImGrad_Mup(n_local_particles);
127 std::vector<double> ReGrad_Mum(n_local_particles);
128 std::vector<double> ImGrad_Mum(n_local_particles);
129
130 for (int ix = -kcut; ix <= +kcut; ix++) {
131 for (int iy = -kcut; iy <= +kcut; iy++) {
132 if (ix == 0 and iy == 0) {
133 continue;
134 }
135 auto const gx = static_cast<double>(ix) * facux;
136 auto const gy = static_cast<double>(iy) * facuy;
137 auto const gr = sqrt(gx * gx + gy * gy);
138
139 // We assume short slab direction is in the z-direction
140 auto const fa1 = 1. / (gr * (exp(gr * box_geo.length()[2]) - 1.));
141
142 // ... Compute S+,(S+)*,S-,(S-)*, and Spj,Smj for the current g
143
144 std::size_t ip = 0;
145 double S[4] = {0., 0., 0., 0.}; // S of Brodka method, or is S[4] =
146 // {Re(S+), Im(S+), Re(S-), Im(S-)}
147 for (auto const &p : particles) {
148 if (p.dipm() != 0.) {
149 auto const &pos = p.pos();
150 auto const dip = p.calc_dip();
151
152 auto const a = gx * dip[0] + gy * dip[1];
153 auto const b = gr * dip[2];
154 auto const er = gx * pos[0] + gy * pos[1];
155 auto const ez = gr * pos[2];
156 auto const c = cos(er);
157 auto const d = sin(er);
158 auto const f = exp(ez);
159
160 ReSjp[ip] = (b * c - a * d) * f;
161 ImSjp[ip] = (c * a + b * d) * f;
162 ReSjm[ip] = (-b * c - a * d) / f;
163 ImSjm[ip] = (c * a - b * d) / f;
164 ReGrad_Mup[ip] = c * f;
165 ReGrad_Mum[ip] = c / f;
166 ImGrad_Mup[ip] = d * f;
167 ImGrad_Mum[ip] = d / f;
168
169 S[0] += ReSjp[ip];
170 S[1] += ImSjp[ip];
171 S[2] += ReSjm[ip];
172 S[3] += ImSjm[ip];
173 }
174 ip++;
175 }
176
177 MPI_Allreduce(MPI_IN_PLACE, S, 4, MPI_DOUBLE, MPI_SUM, comm_cart);
178
179 // ... Now we can compute the contributions to E,Fj,Ej for the current
180 // g-value
181 ip = 0;
182 for (auto &p : particles) {
183 if (p.dipm() != 0.) {
184 {
185 // compute contributions to the forces
186 auto const s1 = -(-ReSjp[ip] * S[3] + ImSjp[ip] * S[2]);
187 auto const s2 = +(ReSjm[ip] * S[1] - ImSjm[ip] * S[0]);
188 auto const s3 = -(-ReSjm[ip] * S[1] + ImSjm[ip] * S[0]);
189 auto const s4 = +(ReSjp[ip] * S[3] - ImSjp[ip] * S[2]);
190
191 auto const s1z = +(ReSjp[ip] * S[2] + ImSjp[ip] * S[3]);
192 auto const s2z = -(ReSjm[ip] * S[0] + ImSjm[ip] * S[1]);
193 auto const s3z = -(ReSjm[ip] * S[0] + ImSjm[ip] * S[1]);
194 auto const s4z = +(ReSjp[ip] * S[2] + ImSjp[ip] * S[3]);
195
196 auto const ss = s1 + s2 + s3 + s4;
197 fs[ip][0] += fa1 * gx * ss;
198 fs[ip][1] += fa1 * gy * ss;
199 fs[ip][2] += fa1 * gr * (s1z + s2z + s3z + s4z);
200 }
201 {
202 // compute contributions to the electrical field
203 auto const s1 = -(-ReGrad_Mup[ip] * S[3] + ImGrad_Mup[ip] * S[2]);
204 auto const s2 = +(ReGrad_Mum[ip] * S[1] - ImGrad_Mum[ip] * S[0]);
205 auto const s3 = -(-ReGrad_Mum[ip] * S[1] + ImGrad_Mum[ip] * S[0]);
206 auto const s4 = +(ReGrad_Mup[ip] * S[3] - ImGrad_Mup[ip] * S[2]);
207
208 auto const s1z = +(ReGrad_Mup[ip] * S[2] + ImGrad_Mup[ip] * S[3]);
209 auto const s2z = -(ReGrad_Mum[ip] * S[0] + ImGrad_Mum[ip] * S[1]);
210 auto const s3z = -(ReGrad_Mum[ip] * S[0] + ImGrad_Mum[ip] * S[1]);
211 auto const s4z = +(ReGrad_Mup[ip] * S[2] + ImGrad_Mup[ip] * S[3]);
212
213 auto const ss = s1 + s2 + s3 + s4;
214 ts[ip][0] += fa1 * gx * ss;
215 ts[ip][1] += fa1 * gy * ss;
216 ts[ip][2] += fa1 * gr * (s1z + s2z + s3z + s4z);
217 }
218 }
219 ++ip;
220 }
221 }
222 }
223
224 // Convert from the corrections to the electrical field to the corrections
225 // for the torques
226 std::size_t ip = 0;
227 for (auto const &p : particles) {
228 if (p.dipm() != 0.) {
229 ts[ip] = vector_product(p.calc_dip(), ts[ip]);
230 }
231 ++ip;
232 }
233
234 // Multiply by the factors we have left during the loops
235
236 auto const piarea =
237 std::numbers::pi * box_geo.length_inv()[0] * box_geo.length_inv()[1];
238 for (std::size_t j = 0; j < n_local_particles; ++j) {
239 fs[j] *= piarea;
240 ts[j] *= piarea;
241 }
242}
243
244/**
245 * @brief Compute the dipolar DLC energy correction.
246 * Algorithm implemented accordingly to @cite brodka04a.
247 */
248static double dipolar_energy_correction(int kcut,
249 ParticleRange const &particles,
250 BoxGeometry const &box_geo) {
251 auto const facux = 2. * std::numbers::pi * box_geo.length_inv()[0];
252 auto const facuy = 2. * std::numbers::pi * box_geo.length_inv()[1];
253
254 double energy = 0.;
255 double sum_S[4] = {0., 0., 0., 0.};
256 for (int ix = -kcut; ix <= +kcut; ix++) {
257 for (int iy = -kcut; iy <= +kcut; iy++) {
258 if ((ix == 0 && iy == 0)) {
259 continue;
260 }
261 auto const gx = static_cast<double>(ix) * facux;
262 auto const gy = static_cast<double>(iy) * facuy;
263 auto const gr = sqrt(gx * gx + gy * gy);
264
265 // We assume short slab direction is in the z-direction
266 auto const fa1 = 1. / (gr * (exp(gr * box_geo.length()[2]) - 1.));
267
268 // ... Compute S+,(S+)*,S-,(S-)*, and Spj,Smj for the current g
269
270 double S[4] = {0., 0., 0., 0.}; // S of Brodka method, or is S[4] =
271 // {Re(S+), Im(S+), Re(S-), Im(S-)}
272 for (auto const &p : particles) {
273 if (p.dipm() != 0.) {
274 auto const &pos = p.pos();
275 auto const dip = p.calc_dip();
276
277 auto const a = gx * dip[0] + gy * dip[1];
278 auto const b = gr * dip[2];
279 auto const er = gx * pos[0] + gy * pos[1];
280 auto const ez = gr * pos[2];
281 auto const c = cos(er);
282 auto const d = sin(er);
283 auto const f = exp(ez);
284
285 S[0] += (b * c - a * d) * f;
286 S[1] += (c * a + b * d) * f;
287 S[2] += (-b * c - a * d) / f;
288 S[3] += (c * a - b * d) / f;
289 }
290 }
291 boost::mpi::reduce(comm_cart, S, 4, sum_S, std::plus<>(), 0);
292
293 // compute contribution to the energy
294 // s2=(ReSm*ReSp+ImSm*ImSp); s2=s1!!!
295 energy += fa1 * 2. * (sum_S[0] * sum_S[2] + sum_S[1] * sum_S[3]);
296 }
297 }
298
299 auto const piarea =
300 std::numbers::pi * box_geo.length_inv()[0] * box_geo.length_inv()[1];
301 energy *= -piarea;
302 return (this_node == 0) ? energy : 0.;
303}
304
306 ParticleRange const &particles) const {
307 assert(dlc.far_cut > 0.);
308 auto const &box_geo = *get_system().box_geo;
309 auto const volume = box_geo.volume();
310 auto const correc = 4. * std::numbers::pi / volume;
311
312 // --- Create arrays that should contain the corrections to
313 // the forces and torques, and set them to zero.
314 std::vector<Utils::Vector3d> dip_DLC_f(particles.size());
315 std::vector<Utils::Vector3d> dip_DLC_t(particles.size());
316
317 //---- Compute the corrections ----------------------------------
318
319 // First the DLC correction
320 auto const kcut = static_cast<int>(std::round(dlc.far_cut));
321 dipolar_force_corrections(kcut, dip_DLC_f, dip_DLC_t, particles, box_geo);
322
323 // Now we compute the correction like Yeh and Klapp to take into account
324 // the fact that you are using a 3D PBC method which uses spherical
325 // summation instead of slab-wise summation.
326 // Slab-wise summation is the one required to apply DLC correction.
327 // This correction is often called SDC = Shape Dependent Correction.
328 // See @cite brodka04a.
329
330 auto const box_dip = calc_slab_dipole(particles);
331
332 // --- Transfer the computed corrections to the Forces, Energy and torques
333 // of the particles
334
335 std::size_t ip = 0;
336 for (auto &p : particles) {
337 check_gap(p);
338
339 if (p.dipm() != 0.) {
340 // SDC correction term is zero for the forces
341 p.force() += prefactor * dip_DLC_f[ip];
342
343 auto const dip = p.calc_dip();
344 // SDC correction for the torques
345 auto d = Utils::Vector3d{0., 0., -correc * box_dip[2]};
346#ifdef DP3M
348 d += correc * epsilon_correction * box_dip;
349 }
350#endif
351 p.torque() += prefactor * (dip_DLC_t[ip] + vector_product(dip, d));
352 }
353 ++ip;
354 }
355}
356
358 ParticleRange const &particles) const {
359 assert(dlc.far_cut > 0.);
360 auto const &box_geo = *get_system().box_geo;
361 auto const volume = box_geo.volume();
362 auto const pref = prefactor * 2. * std::numbers::pi / volume;
363
364 // Check if particles aren't in the forbidden gap region
365 // This loop is needed, because there is no other guaranteed
366 // single pass over all particles in this function.
367 for (auto const &p : particles) {
368 check_gap(p);
369 }
370
371 //---- Compute the corrections ----------------------------------
372
373 // First the DLC correction
374 auto const k_cut = static_cast<int>(std::round(dlc.far_cut));
375 auto dip_DLC_energy =
376 prefactor * dipolar_energy_correction(k_cut, particles, box_geo);
377
378 // Now we compute the correction like Yeh and Klapp to take into account
379 // the fact that you are using a 3D PBC method which uses spherical
380 // summation instead of slab-wise summation.
381 // Slab-wise summation is the one required to apply DLC correction.
382 // This correction is often called SDC = Shape Dependent Correction.
383 // See @cite brodka04a.
384
385 auto const box_dip = calc_slab_dipole(particles);
386
387 if (this_node == 0) {
388 dip_DLC_energy += pref * Utils::sqr(box_dip[2]);
389#ifdef DP3M
391 dip_DLC_energy -= pref * epsilon_correction * box_dip.norm2();
392 }
393#endif
394 return dip_DLC_energy;
395 }
396 return 0.;
397}
398
399static int count_magnetic_particles(System::System const &system) {
400 int local_n = 0;
401
402 for (auto const &p : system.cell_structure->local_particles()) {
403 if (p.dipm() != 0.) {
404 local_n++;
405 }
406 }
407
408 return boost::mpi::all_reduce(comm_cart, local_n, std::plus<>());
409}
410
411/** Compute the cut-off in the DLC dipolar part to get a certain accuracy.
412 * We assume particles to have all the same value of the dipolar momentum
413 * modulus, which is taken as the largest value of mu inside the system.
414 * If we assume the gap has a width @c gap_size (within
415 * which there is no particles): <tt>Lz = h + gap_size</tt>
416 */
417double DipolarLayerCorrection::tune_far_cut() const {
418 auto const &system = get_system();
419 /* we take the maximum dipole in the system, to be sure that the errors
420 * in the other case will be equal or less than for this one */
421 auto const mu_max_sq = Utils::sqr(calc_mu_max(system));
422 auto const &box_geo = *system.box_geo;
423 auto const lx = box_geo.length()[0];
424 auto const ly = box_geo.length()[1];
425 auto const lz = box_geo.length()[2];
426
427 if (std::abs(lx - ly) > 0.001) {
428 throw std::runtime_error("DLC tuning: box size in x direction is "
429 "different from y direction. The tuning "
430 "formula requires both to be equal.");
431 }
432
433 auto constexpr limitkc = 200;
434 auto constexpr exp_max = +709.8; // for IEEE-compatible double
435 auto constexpr exp_min = -708.4; // for IEEE-compatible double
436 auto const log_max = std::log(std::numeric_limits<double>::max());
437 auto const piarea = std::numbers::pi / (lx * ly);
438 auto const nmp = static_cast<double>(count_magnetic_particles(system));
439 auto const h = dlc.box_h;
440 auto far_cut = -1.;
441 for (int kc = 1; kc < limitkc; kc++) {
442 auto const gc = kc * 2. * std::numbers::pi / lx;
443 auto const pref1 = g1_DLC_dip(gc, lz - h);
444 auto const pref2 = g1_DLC_dip(gc, lz + h);
445 auto const pref0 = g1_DLC_dip(gc, lz);
446 auto const exp_term = 2. * gc * h;
447 auto const log_term1 = exp_term + std::log(9. * pref1);
448 if (exp_term >= exp_max or log_term1 >= log_max or -exp_term <= exp_min) {
449 // no solution can be found at larger k values due to overflow/underflow
450 break;
451 }
452 auto const fa0 = std::sqrt(9. * std::exp(+exp_term) * pref1 +
453 9. * std::exp(-exp_term) * pref2 + 22. * pref0);
454 auto const fa1 = sqrt(0.125 * piarea) * fa0;
455 auto const fa2 = g2_DLC_dip(gc, lz);
456 auto const de = nmp * mu_max_sq / (4. * (exp(gc * lz) - 1.)) * (fa1 + fa2);
457 if (de < dlc.maxPWerror) {
458 far_cut = static_cast<double>(kc);
459 break;
460 }
461 }
462 if (far_cut <= 0.) {
463 throw std::runtime_error("DLC tuning failed: maxPWerror too small");
464 }
465 return far_cut;
466}
467
468/** @brief Lock an actor and modify its parameters. */
470 explicit AdaptSolver(DipolarLayerCorrection *this_ptr) : m_actor{this_ptr} {}
471
472 void operator()(std::shared_ptr<DipolarDirectSum> const &solver) {
473 m_actor->prefactor = solver->prefactor;
474 m_actor->epsilon = P3M_EPSILON_METALLIC;
475 }
476
477#ifdef DP3M
478 void operator()(std::shared_ptr<DipolarP3M> const &solver) {
479 m_actor->prefactor = solver->prefactor;
480 m_actor->epsilon = solver->dp3m_params.epsilon;
481 }
482#endif
483
484private:
485 DipolarLayerCorrection *m_actor;
486};
487
489 auto visitor = AdaptSolver{this};
490 std::visit(visitor, base_solver);
492 (epsilon == P3M_EPSILON_METALLIC) ? 0. : 1. / (2. * epsilon + 1.);
493}
494
495void DipolarLayerCorrection::sanity_checks_node_grid() const {
496 auto const &box_geo = *get_system().box_geo;
497 if (!box_geo.periodic(0) || !box_geo.periodic(1) || !box_geo.periodic(2)) {
498 throw std::runtime_error("DLC: requires periodicity (True, True, True)");
499 }
500}
501
502dlc_data::dlc_data(double maxPWerror, double gap_size, double far_cut)
503 : maxPWerror{maxPWerror}, gap_size{gap_size}, box_h{-1.}, far_cut{far_cut},
504 far_calculated{far_cut == -1.} {
505 if (far_cut <= 0. and not far_calculated) {
506 throw std::domain_error("Parameter 'far_cut' must be > 0");
507 }
508 if (maxPWerror <= 0.) {
509 throw std::domain_error("Parameter 'maxPWerror' must be > 0");
510 }
511 if (gap_size <= 0.) {
512 throw std::domain_error("Parameter 'gap_size' must be > 0");
513 }
514}
515
517 auto const lz = get_system().box_geo->length()[2];
518 auto const new_box_h = lz - dlc.gap_size;
519 if (new_box_h < 0.) {
520 throw std::runtime_error("DLC gap size (" + std::to_string(dlc.gap_size) +
521 ") larger than box length in z-direction (" +
522 std::to_string(lz) + ")");
523 }
524 dlc.box_h = new_box_h;
525}
526
528 BaseSolver &&solver)
529 : dlc{parameters}, base_solver{solver} {
530 adapt_solver();
531}
532
533#endif // DIPOLES
Utils::Vector3d const & length() const
Box length.
Utils::Vector3d const & length_inv() const
Inverse box length.
A range of particles.
base_type::size_type size() const
Main system class.
std::shared_ptr< CellStructure > cell_structure
boost::mpi::communicator comm_cart
The communicator.
int this_node
The number of this node.
This file contains the defaults for ESPResSo.
__device__ void vector_product(float const *a, float const *b, float *out)
static void dipolar_force_corrections(int kcut, std::vector< Utils::Vector3d > &fs, std::vector< Utils::Vector3d > &ts, ParticleRange const &particles, BoxGeometry const &box_geo)
Compute the dipolar force and torque corrections.
Definition dlc.cpp:112
static double g1_DLC_dip(double g, double x)
Definition dlc.cpp:81
static double dipolar_energy_correction(int kcut, ParticleRange const &particles, BoxGeometry const &box_geo)
Compute the dipolar DLC energy correction.
Definition dlc.cpp:248
static double g2_DLC_dip(double g, double x)
Definition dlc.cpp:89
static Utils::Vector3d calc_slab_dipole(ParticleRange const &particles)
Compute the box magnetic dipole.
Definition dlc.cpp:96
static double calc_mu_max(System::System const &system)
Calculate the maximal dipole moment in the system.
Definition dlc.cpp:71
static int count_magnetic_particles(System::System const &system)
Definition dlc.cpp:399
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 ...
#define runtimeErrorMsg()
DEVICE_QUALIFIER constexpr T sqr(T x)
Calculates the SQuaRe of x.
Definition sqr.hpp:28
Vector< T, N > sqrt(Vector< T, N > const &a)
Definition Vector.hpp:357
Common functions for dipolar and charge P3M.
auto constexpr P3M_EPSILON_METALLIC
This value indicates metallic boundary conditions.
Lock an actor and modify its parameters.
Definition dlc.cpp:469
AdaptSolver(DipolarLayerCorrection *this_ptr)
Definition dlc.cpp:470
void operator()(std::shared_ptr< DipolarDirectSum > const &solver)
Definition dlc.cpp:472
void operator()(std::shared_ptr< DipolarP3M > const &solver)
Definition dlc.cpp:478
Adapt a magnetostatics solver to remove contributions from the z-direction.
Definition dlc.hpp:78
double epsilon_correction
Definition dlc.hpp:89
double energy_correction(ParticleRange const &particles) const
Calculate the dipolar energy correction.
Definition dlc.cpp:357
BaseSolver base_solver
Magnetostatics solver that is adapted.
Definition dlc.hpp:94
void add_force_corrections(ParticleRange const &particles) const
Add the dipolar force and torque corrections.
Definition dlc.cpp:305
DipolarLayerCorrection(dlc_data &&parameters, BaseSolver &&solver)
Definition dlc.cpp:527
std::variant< std::shared_ptr< DipolarP3M >, std::shared_ptr< DipolarDirectSum > > BaseSolver
Definition dlc.hpp:83
Struct holding all information for one particle.
Definition Particle.hpp:395
auto calc_dip() const
Definition Particle.hpp:495
auto const & torque() const
Definition Particle.hpp:479
auto const & dipm() const
Definition Particle.hpp:493
auto const & pos() const
Definition Particle.hpp:431
auto const & force() const
Definition Particle.hpp:435
auto const & id() const
Definition Particle.hpp:414
Parameters for the DLC method.
Definition dlc.hpp:47
bool far_calculated
Flag whether far_cut was set by the user, or calculated by ESPResSo.
Definition dlc.hpp:71
dlc_data(double maxPWerror, double gap_size, double far_cut)
Definition dlc.cpp:502
double maxPWerror
maximal pairwise error of the potential and force
Definition dlc.hpp:51
double far_cut
Cutoff of the exponential sum.
Definition dlc.hpp:65
double gap_size
Size of the empty gap.
Definition dlc.hpp:56
double box_h
Up to where particles can be found.
Definition dlc.hpp:59