ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
elc.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2010-2026 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 * @brief ELC algorithm for long-range Coulomb interactions.
24 *
25 * Implementation of the ELC method for the calculation of the electrostatic
26 * interaction in two dimensional periodic systems. For details on the method
27 * see MMM in general. The ELC method works together with any three-dimensional
28 * method, for example @ref p3m.hpp "P3M", with metallic boundary conditions.
29 */
30
31#pragma once
32
33#include <config/config.hpp>
34
35#ifdef ESPRESSO_P3M
36
37#include "actor/traits.hpp"
38
40
41#include "BoxGeometry.hpp"
42#include "Particle.hpp"
43#include "ParticleRange.hpp"
44
45#include <utils/Vector.hpp>
46#include <utils/math/sqr.hpp>
47
48#include <cstddef>
49#include <memory>
50#include <optional>
51#include <string>
52#include <type_traits>
53#include <utility>
54#include <variant>
55
57
58namespace traits {
59template <>
61} // namespace traits
62
63/** @brief Parameters for the ELC method */
64struct elc_data {
65 elc_data(double maxPWerror, double gap_size, double far_cut, bool neutralize,
66 double delta_top, double delta_bot, bool const_pot, double pot_diff);
67
68 /**
69 * @brief Maximal allowed pairwise error for the potential and force.
70 * Note that this counts for the plain 1/r contribution
71 * alone, without the prefactor and the charge prefactor.
72 */
73 double maxPWerror;
74 /** Size of the empty gap. Note that ELC relies on the user to make sure
75 * that this condition is fulfilled.
76 */
77 double gap_size;
78 /** Up to where particles can be found. */
79 double box_h;
80 /**
81 * @brief Cutoff of the exponential sum.
82 * Since in all other MMM methods this is the far formula,
83 * it is given the same name here.
84 */
85 double far_cut;
86 /** Squared value of #far_cut. */
87 double far_cut2;
88 /** Flag whether #far_cut was set by the user, or calculated by ESPResSo.
89 * In the latter case, the cutoff will be adapted if important parameters,
90 * such as the box dimensions, change.
91 */
93
94 /// @brief Flag whether there is any dielectric contrast in the system.
96 /// @brief Flag whether a constant potential difference is applied.
98 /**
99 * @brief Flag whether the box is neutralized by a homogeneous background.
100 * If true, use a homogeneous neutralizing background for non-neutral
101 * systems. Unlike the 3D case, this background adds an additional
102 * force pointing towards the system center, and the gap size
103 * enters into the value of the forces, so be careful with this.
104 */
106
107 /// dielectric contrast in the upper part of the simulation cell.
109 /// dielectric contrast in the lower part of the simulation cell.
111 /// @brief Constant potential difference.
112 double pot_diff;
113
114 /** Layer around the dielectric contrast in which we trick around. */
116 /** The space that is finally left. */
117 double space_box;
118
119#ifdef ESPRESSO_SHARED_MEMORY_PARALLELISM
120 /// pairwise contributions from lower and upper layers
122 std::size_t p1, std::size_t p2,
123 auto &aosoa, double q1q2,
124 auto &&kernel) const {
125 if (aosoa.position(p1, 2) < space_layer) {
126 auto const q_eff = delta_mid_bot * q1q2;
127 auto pos2 = aosoa.get_vector_at(aosoa.position, p2);
128 auto pos1 = aosoa.get_vector_at(aosoa.position, p1);
129 pos1[2] *= -1.;
130 auto const d = box_geo.get_mi_vector(pos2, pos1);
131 kernel(q_eff, d);
132 }
133 if (aosoa.position(p1, 2) > (box_h - space_layer)) {
134 auto const q_eff = delta_mid_top * q1q2;
135 auto const z = 2. * box_h - aosoa.position(p1, 2);
136 auto pos2 = aosoa.get_vector_at(aosoa.position, p2);
137 auto pos1 = aosoa.get_vector_at(aosoa.position, p1);
138 pos1[2] = 2. * box_h - pos1[2];
139 auto const d = box_geo.get_mi_vector(pos2, pos1);
140 kernel(q_eff, d);
141 }
142 }
143#endif // ESPRESSO_SHARED_MEMORY_PARALLELISM
144
145 /// pairwise contributions from lower and upper layers
147 Utils::Vector3d const &pos1,
148 Utils::Vector3d const &pos2, double q1q2,
149 auto &&kernel) const {
150 if (pos1[2] < space_layer) {
151 auto const q_eff = delta_mid_bot * q1q2;
152 auto const d = box_geo.get_mi_vector(pos2, {pos1[0], pos1[1], -pos1[2]});
153 kernel(q_eff, d);
154 }
155 if (pos1[2] > (box_h - space_layer)) {
156 auto const q_eff = delta_mid_top * q1q2;
157 auto const z = 2. * box_h - pos1[2];
158 auto const d = box_geo.get_mi_vector(pos2, {pos1[0], pos1[1], z});
159 kernel(q_eff, d);
160 }
161 }
162
163 /// self energies of top and bottom layers with their virtual images
165 BoxGeometry const &box_geo,
166 ParticleRange const &particles) const {
167 auto energy = 0.;
168 for (auto const &p : particles) {
170 box_geo, p.pos(), p.pos(), Utils::sqr(p.q()),
171 [&](double q1q2, Utils::Vector3d const &d) {
172 energy += p3m.pair_energy(q1q2, d.norm());
173 });
174 }
175 return energy;
176 }
177
178 /// forces of particles in border layers with themselves
180 BoxGeometry const &box_geo,
181 ParticleRange const &particles) const {
182 for (auto &p : particles) {
184 box_geo, p.pos(), p.pos(), Utils::sqr(p.q()),
185 [&](double q1q2, Utils::Vector3d const &d) {
186 p.force() += p3m.pair_force(q1q2, d, d.norm());
187 });
188 }
189 }
190};
191
193 : public Coulomb::Actor<ElectrostaticLayerCorrection> {
194 using BaseSolver = std::variant<std::shared_ptr<CoulombP3M>>;
195
198
199 /** Electrostatics solver that is adapted. */
201
203
205 visit_base_solver(
206 [this](auto &solver) { solver->bind_system(m_system.lock()); });
207 sanity_checks_periodicity();
208 sanity_checks_cell_structure();
210 sanity_checks_dielectric_contrasts();
211 /* Most ELC parameters do not depend on the P3M parameters,
212 * but the P3M parameters depend on the ELC parameters during tuning,
213 * therefore ELC needs to be tuned before P3M. */
214 recalc_box_h();
215 recalc_far_cut();
216 visit_base_solver([](auto &solver) { solver->on_activation(); });
217 /* With dielectric contrasts, the ELC space layer depends
218 * on the P3M real-space cutoff, and the P3M FFT parameters
219 * depend on the ELC space layer */
221 recalc_space_layer();
222 visit_base_solver([](auto &actor) { actor->init(); });
223 }
224 }
225 /** @brief Recalculate all box-length-dependent parameters. */
227 visit_base_solver([](auto &actor) { actor->on_boxl_change(); });
228 recalc_box_h();
229 recalc_far_cut();
230 recalc_space_layer();
231 }
232 void on_node_grid_change() const {
233 visit_base_solver([](auto &solver) { solver->on_node_grid_change(); });
234 }
236 sanity_checks_periodicity();
237 visit_base_solver([](auto &solver) { solver->on_periodicity_change(); });
238 }
240 sanity_checks_cell_structure();
241 visit_base_solver([](auto &solver) { solver->on_cell_structure_change(); });
242 recalc_box_h();
243 recalc_far_cut();
245 recalc_space_layer();
246 visit_base_solver([](auto &actor) { actor->init(); });
247 }
248 }
249 /** @brief Recalculate all derived parameters. */
250 void init() {
251 visit_base_solver([](auto &actor) { actor->init(); });
252 recalc_box_h();
253 recalc_far_cut();
254 recalc_space_layer();
255 }
256
257 void sanity_checks() const {
258 sanity_checks_periodicity();
259 sanity_checks_cell_structure();
261 sanity_checks_dielectric_contrasts();
262 visit_base_solver([](auto &actor) { actor->sanity_checks(); });
263 }
264
265 /**
266 * @brief Veto real-space cutoff values that are incompatible with ELC.
267 * When ELC is used with dielectric contrasts, the short-range cutoff needs
268 * to be smaller than the gap size to allow placement of the image charges.
269 */
270 std::optional<std::string> veto_r_cut(double r_cut) const {
272 return {std::string("conflict with ELC w/ dielectric contrasts")};
273 }
274 return {};
275 }
276
278 double dist) const {
279 return std::visit(
280 [&](auto &solver) { return solver->pair_force(q1q2, d, dist); },
282 }
283
284#ifdef ESPRESSO_SHARED_MEMORY_PARALLELISM
285 /** @brief Calculate short-range pair energy correction. */
286 double pair_energy_correction(std::size_t p1, std::size_t p2, auto &aosoa,
287 double q1q2) const {
288 double energy = 0.;
290 energy = std::visit(
291 [this, &aosoa, p1, p2, q1q2](auto &p3m_ptr) {
292 auto const &p3m = *p3m_ptr;
293 auto energy = 0.;
295 *m_box_geo, p1, p2, aosoa, q1q2,
296 [&](double q_eff, Utils::Vector3d const &d) {
297 energy += p3m.pair_energy(q_eff, d.norm());
298 });
300 *m_box_geo, p2, p1, aosoa, q1q2,
301 [&](double q_eff, Utils::Vector3d const &d) {
302 energy += p3m.pair_energy(q_eff, d.norm());
303 });
304 return energy / 2.;
305 },
307 }
308 return energy;
309 }
310#endif // ESPRESSO_SHARED_MEMORY_PARALLELISM
311
312 /** @brief Calculate short-range pair energy correction. */
314 Utils::Vector3d const &pos2,
315 double q1q2) const {
316 double energy = 0.;
318 energy = std::visit(
319 [this, &pos1, &pos2, q1q2](auto &p3m_ptr) {
320 auto const &p3m = *p3m_ptr;
321 auto energy = 0.;
323 *m_box_geo, pos1, pos2, q1q2,
324 [&](double q_eff, Utils::Vector3d const &d) {
325 energy += p3m.pair_energy(q_eff, d.norm());
326 });
328 *m_box_geo, pos2, pos1, q1q2,
329 [&](double q_eff, Utils::Vector3d const &d) {
330 energy += p3m.pair_energy(q_eff, d.norm());
331 });
332 return energy / 2.;
333 },
335 }
336 return energy;
337 }
338
339 /** @brief Add short-range pair force corrections. */
341 Utils::Vector3d const &pos2,
344 double q1q2) const {
346 std::visit(
347 [this, &pos1, &pos2, &p1f_asym, &p2f_asym, q1q2](auto &p3m_ptr) {
348 auto const &p3m = *p3m_ptr;
350 *m_box_geo, pos1, pos2, q1q2,
351 [&](double q_eff, Utils::Vector3d const &d) {
352 p1f_asym += p3m.pair_force(q_eff, d, d.norm());
353 });
355 *m_box_geo, pos2, pos1, q1q2,
356 [&](double q_eff, Utils::Vector3d const &d) {
357 p2f_asym += p3m.pair_force(q_eff, d, d.norm());
358 });
359 },
361 }
362 }
363
364 /** @brief Calculate long-range electrostatic energy with corrections. */
365 double long_range_energy() const;
366 /** @brief Accumulate long-range electrostatic forces with corrections. */
367 void add_long_range_forces() const;
368
369private:
370 /** Check if a charged particle is in the gap region. */
371 void check_gap(Particle const &p) const;
372 double tune_far_cut() const;
373 void adapt_solver();
374 /** pairwise contributions from the lowest and top layers to the energy */
375 double dipole_energy() const;
376 void add_dipole_force() const;
377 double z_energy() const;
378 void add_z_force() const;
379
380 void recalc_box_h();
381 void recalc_far_cut() {
382 if (elc.far_calculated) {
383 elc.far_cut = tune_far_cut();
384 }
386 }
387 void recalc_space_layer();
388
389 void sanity_checks_cell_structure() const {}
390 void sanity_checks_periodicity() const;
391 void sanity_checks_dielectric_contrasts() const;
392
393 /// the force calculation
394 void add_force() const;
395 /// the energy calculation
396 double calc_energy() const;
397
398 void visit_base_solver(auto &&visitor) const {
399 std::visit(visitor, base_solver);
400 }
401};
402
403#endif // ESPRESSO_P3M
Vector implementation and trait types for boost qvm interoperability.
ESPRESSO_ATTR_ALWAYS_INLINE Utils::Vector3< T > get_mi_vector(Utils::Vector3< T > const &a, Utils::Vector3< T > const &b) const
Get the minimum-image vector between two coordinates.
A range of particles.
std::weak_ptr< System > m_system
T norm() const
Definition Vector.hpp:159
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
DEVICE_QUALIFIER constexpr T sqr(T x)
Calculates the SQuaRe of x.
Definition sqr.hpp:28
P3M algorithm for long-range Coulomb interaction.
P3M solver.
Definition p3m.hpp:55
void on_boxl_change()
Recalculate all box-length-dependent parameters.
Definition elc.hpp:226
void on_periodicity_change() const
Definition elc.hpp:235
void sanity_checks() const
Definition elc.hpp:257
void add_long_range_forces() const
Accumulate long-range electrostatic forces with corrections.
Definition elc.cpp:1253
std::optional< std::string > veto_r_cut(double r_cut) const
Veto real-space cutoff values that are incompatible with ELC.
Definition elc.hpp:270
std::variant< std::shared_ptr< CoulombP3M > > BaseSolver
Definition elc.hpp:194
BaseSolver base_solver
Electrostatics solver that is adapted.
Definition elc.hpp:200
double pair_energy_correction(Utils::Vector3d const &pos1, Utils::Vector3d const &pos2, double q1q2) const
Calculate short-range pair energy correction.
Definition elc.hpp:313
Utils::Vector3d pair_force(double q1q2, Utils::Vector3d const &d, double dist) const
Definition elc.hpp:277
void add_pair_force_corrections(Utils::Vector3d const &pos1, Utils::Vector3d const &pos2, Utils::Vector3d &p1f_asym, Utils::Vector3d &p2f_asym, double q1q2) const
Add short-range pair force corrections.
Definition elc.hpp:340
void init()
Recalculate all derived parameters.
Definition elc.hpp:250
double long_range_energy() const
Calculate long-range electrostatic energy with corrections.
Definition elc.cpp:1210
void on_node_grid_change() const
Definition elc.hpp:232
BoxGeometry * m_box_geo
Definition elc.hpp:197
double pair_energy_correction(std::size_t p1, std::size_t p2, auto &aosoa, double q1q2) const
Calculate short-range pair energy correction.
Definition elc.hpp:286
Struct holding all information for one particle.
Definition Particle.hpp:435
Parameters for the ELC method.
Definition elc.hpp:64
double dielectric_layers_self_energy(CoulombP3M const &p3m, BoxGeometry const &box_geo, ParticleRange const &particles) const
self energies of top and bottom layers with their virtual images
Definition elc.hpp:164
double maxPWerror
Maximal allowed pairwise error for the potential and force.
Definition elc.hpp:73
double pot_diff
Constant potential difference.
Definition elc.hpp:112
double box_h
Up to where particles can be found.
Definition elc.hpp:79
void dielectric_layers_contribution(BoxGeometry const &box_geo, std::size_t p1, std::size_t p2, auto &aosoa, double q1q2, auto &&kernel) const
pairwise contributions from lower and upper layers
Definition elc.hpp:121
bool dielectric_contrast_on
Flag whether there is any dielectric contrast in the system.
Definition elc.hpp:95
double space_box
The space that is finally left.
Definition elc.hpp:117
void dielectric_layers_contribution(BoxGeometry const &box_geo, Utils::Vector3d const &pos1, Utils::Vector3d const &pos2, double q1q2, auto &&kernel) const
pairwise contributions from lower and upper layers
Definition elc.hpp:146
bool neutralize
Flag whether the box is neutralized by a homogeneous background.
Definition elc.hpp:105
double far_cut
Cutoff of the exponential sum.
Definition elc.hpp:85
double space_layer
Layer around the dielectric contrast in which we trick around.
Definition elc.hpp:115
bool far_calculated
Flag whether far_cut was set by the user, or calculated by ESPResSo.
Definition elc.hpp:92
double gap_size
Size of the empty gap.
Definition elc.hpp:77
double delta_mid_bot
dielectric contrast in the lower part of the simulation cell.
Definition elc.hpp:110
void dielectric_layers_self_forces(CoulombP3M const &p3m, BoxGeometry const &box_geo, ParticleRange const &particles) const
forces of particles in border layers with themselves
Definition elc.hpp:179
bool const_pot
Flag whether a constant potential difference is applied.
Definition elc.hpp:97
double far_cut2
Squared value of far_cut.
Definition elc.hpp:87
double delta_mid_top
dielectric contrast in the upper part of the simulation cell.
Definition elc.hpp:108
Whether an actor is a layer correction method.
Definition traits.hpp:26