ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
p3m/common.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 * Common functions for dipolar and charge P3M.
24 *
25 * We use here a P3M (Particle-Particle Particle-Mesh) method based
26 * on the Ewald summation. Details of the used method can be found in
27 * @cite hockney88a and @cite deserno98a @cite deserno98b. The file p3m
28 * contains only the Particle-Mesh part.
29 *
30 * Further reading: @cite ewald21a, @cite hockney88a, @cite deserno98a,
31 * @cite deserno98b, @cite deserno00e, @cite deserno00b, @cite cerda08d
32 *
33 */
34
35#pragma once
36
37#include <config/config.hpp>
38
39#include <utils/Vector.hpp>
40#include <utils/index.hpp>
41
42#include <algorithm>
43#include <array>
44#include <vector>
45
46/** This value indicates metallic boundary conditions. */
47inline auto constexpr P3M_EPSILON_METALLIC = 0.0;
48
49#if defined(ESPRESSO_P3M) or defined(ESPRESSO_DP3M)
50
51#include "LocalBox.hpp"
52
53#include <Kokkos_Core.hpp>
54
55#include <cstddef>
56#include <optional>
57#include <span>
58#include <stdexcept>
59
60/** @brief P3M kernel architecture. */
61enum class Arch { CPU, CUDA };
62
63/** @brief Structure to hold P3M parameters and some dependent variables. */
65 /** tuning or production? */
66 bool tuning;
67 /** Ewald splitting parameter (0<alpha<1), rescaled to
68 * @p alpha_L = @p alpha * @p box_l. */
69 double alpha_L;
70 /** cutoff radius for real space electrostatics (>0), rescaled to
71 * @p r_cut_iL = @p r_cut * @p box_l_i. */
72 double r_cut_iL;
73 /** number of mesh points per coordinate direction (>0), in real space. */
75 /** offset of the first mesh point (lower left corner) from the
76 * coordinate origin ([0,1[). */
78 /** charge assignment order ([0,7]). */
79 int cao;
80 /** accuracy of the actual parameter set. */
81 double accuracy;
82
83 /** epsilon of the "surrounding dielectric". */
84 double epsilon;
85 /** cutoff for charge assignment. */
87 /** mesh constant. */
89 /** inverse mesh constant. */
91 /** unscaled @ref P3MParameters::alpha_L "alpha_L" for use with fast
92 * inline functions only */
93 double alpha;
94 /** unscaled @ref P3MParameters::r_cut_iL "r_cut_iL" for use with fast
95 * inline functions only */
96 double r_cut;
97 /** number of points unto which a single charge is interpolated, i.e.
98 * @ref P3MParameters::cao "cao" cubed */
99 int cao3;
100
101 P3MParameters(bool tuning, double epsilon, double r_cut,
103 int cao, double alpha, double accuracy)
104 : tuning{tuning}, alpha_L{0.}, r_cut_iL{0.}, mesh{mesh},
106 cao_cut{}, a{}, ai{}, alpha{alpha}, r_cut{r_cut}, cao3{-1} {
107
108 auto constexpr value_to_tune = -1.;
109
110 if (epsilon < 0.) {
111 throw std::domain_error("Parameter 'epsilon' must be >= 0");
112 }
113
114 if (accuracy <= 0.) {
115 throw std::domain_error("Parameter 'accuracy' must be > 0");
116 }
117
118 if (r_cut <= 0.) {
119 if (tuning and r_cut == value_to_tune) {
120 this->r_cut = 0.;
121 } else {
122 throw std::domain_error("Parameter 'r_cut' must be > 0");
123 }
124 }
125
126 if (alpha <= 0.) {
127 if (tuning and alpha == value_to_tune) {
128 this->alpha = 0.;
129 } else {
130 throw std::domain_error("Parameter 'alpha' must be > 0");
131 }
132 }
133
135 ((mesh[0] >= 1) and (mesh == Utils::Vector3i{{mesh[0], -1, -1}})) or
137 throw std::domain_error("Parameter 'mesh' must be > 0");
138 }
139
143 this->mesh_off = Utils::Vector3d::broadcast(0.5);
144 } else {
145 throw std::domain_error("Parameter 'mesh_off' must be >= 0 and <= 1");
146 }
147 }
148
149 if ((cao < 1 or cao > 7) and (not tuning or cao != -1)) {
150 throw std::domain_error("Parameter 'cao' must be >= 1 and <= 7");
151 }
152
154 throw std::domain_error("Parameter 'cao' cannot be larger than 'mesh'");
155 }
156 }
157
158 /**
159 * @brief Recalculate quantities derived from the mesh and box length:
160 * @ref P3MParameters::a "a",
161 * @ref P3MParameters::ai "ai" and
162 * @ref P3MParameters::cao_cut "cao_cut".
163 */
167 cao_cut = (static_cast<double>(cao) / 2.) * a;
168 }
169
170 /**
171 * @brief Convert spatial position to grid position.
172 * To get the grid index, round the result to the nearest integer.
173 */
174 auto calc_grid_pos(Utils::Vector3d const &pos) const {
175 return Utils::hadamard_product(pos, ai) - mesh_off;
176 }
177};
178
179/**
180 * @brief Properties of the local mesh and halo region.
181 * The thickness of the halo region depends on the following parameters:
182 * - The half-width of the charge-assignment stencil (cao_cut = cao/2*a).
183 * A charge at position (0,0,0) is spread onto a cube of width cao, hence
184 * the half-width must be padded on both sides of the local domain.
185 * - The Verlet list skin must be added extra, since particles are not resorted
186 * at every time step, but rather when they moved more than one skin.
187 * - The ELC space layer is also needed when dielectric contrast is active,
188 * since real charges within `space_layer` of the bottom wall induce image
189 * charges outside the simulation box, hence the halo region is thicker
190 * in the non-periodic direction.
191 */
193 /** dimension (size) of local mesh including halo layers. */
196 /** number of local mesh points including halo layers. */
197 std::size_t size;
198 /** index of lower left corner of the
199 local mesh in the global mesh. */
201 /** position of the first local mesh point. */
205 /** dimension of mesh inside node domain. */
207 /** inner left down grid point */
209 /** inner up right grid point + (1,1,1) */
211 /** number of margin mesh points. */
212 int margin[6]; // !! legacy
215 /** number of margin mesh points from neighbour nodes */
216 int r_margin[6];
217 /** offset between mesh lines of the last dimension */
219 /** offset between mesh lines of the two last dimensions */
221
222 /**
223 * @brief Recalculate quantities derived from the mesh and box length:
224 * @ref P3MLocalMesh::ld_pos "ld_pos" (position of the left down mesh).
225 */
226 void recalc_ld_pos(P3MParameters const &params) {
227 // spatial position of left down mesh point
228 for (auto i = 0u; i < 3u; i++) {
229 ld_pos[i] = (ld_ind[i] + params.mesh_off[i]) * params.a[i];
230 }
231 }
232
233 /**
234 * @brief Calculate properties of the local FFT mesh
235 * for the charge assignment process.
236 */
237 void calc_local_ca_mesh(P3MParameters const &params,
238 LocalBox const &local_geo, double skin,
239 double space_layer);
240};
241
242/** @brief Local mesh FFT buffers. */
243template <typename FloatType> struct P3MFFTMesh {
244 /** @brief real-space scalar mesh for charge assignment and FFT. */
245 std::span<FloatType> rs_scalar;
246 /** @brief real-space scalar charge density. */
247 std::span<FloatType> rs_charge_density;
248
249 /** @brief real-space vector meshes for the electric or dipolar field. */
250 std::array<std::span<FloatType>, 3> rs_fields;
251
252 /** @brief Indices of the lower left corner of the local mesh grid. */
254 /** @brief Indices of the upper right corner of the local mesh grid. */
256 /** @brief Extents of the local mesh grid. */
258
259 /** @brief number of permutations in k_space */
260 int ks_pnum = 0;
261};
262
265 std::pair<std::optional<int>, std::optional<int>> limits;
267};
268
269/**
270 * @brief Adapt an influence function grid for real-to-complex FFTs.
271 * @param[in] global_size size of the global mesh grid
272 * @param[in] local_size size of the local mesh grid
273 * @param[in] local_origin offset of the local mesh grid
274 * @param[in,out] g_function influence function grid to modify in-place
275 * @tparam r2c_dir direction of the reduced dimension
276 */
277template <unsigned int r2c_dir>
279 auto const &local_size, auto const &local_origin) {
280 auto const cutoff_right = global_size[r2c_dir] / 2 - local_origin[r2c_dir];
281 std::remove_cvref_t<decltype(g_function)> g_function_r2c;
282 g_function_r2c.reserve(g_function.size() / 2ul);
284 auto &short_dim = local_index[r2c_dir];
285 auto &nx = local_index[0u];
286 auto &ny = local_index[1u];
287 auto &nz = local_index[2u];
288 std::size_t index = 0u;
289 for (nx = 0; nx < local_size[0u]; ++nx) {
290 for (ny = 0; ny < local_size[1u]; ++ny) {
291 for (nz = 0; nz < local_size[2u]; ++nz) {
292 if (short_dim <= cutoff_right) {
293 g_function_r2c.emplace_back(g_function[index]);
294 }
295 ++index;
296 }
297 }
298 }
299 std::swap(g_function, g_function_r2c);
300}
301
302#endif // defined(ESPRESSO_P3M) or defined(ESPRESSO_DP3M)
303
304/** @brief Calculate indices that shift @ref P3MParameters::mesh by `mesh/2`.
305 * For each mesh size @f$ n @f$ in @c mesh_size, create a sequence of integer
306 * values @f$ \left( 0, \ldots, \lfloor n/2 \rfloor, -\lfloor n/2 \rfloor,
307 * \ldots, -1\right) @f$ if @c zero_out_midpoint is false, otherwise
308 * @f$ \left( 0, \ldots, \lfloor n/2 - 1 \rfloor, 0, -\lfloor n/2 \rfloor,
309 * \ldots, -1\right) @f$.
310 */
311std::array<std::vector<int>, 3> inline calc_p3m_mesh_shift(
312 Utils::Vector3i const &mesh_size, bool zero_out_midpoint = false) {
313 std::array<std::vector<int>, 3> ret{};
314
315 for (auto i = 0u; i < 3u; ++i) {
316 ret[i] = std::vector<int>(static_cast<std::size_t>(mesh_size[i]));
317
318 for (int j = 1; j <= mesh_size[i] / 2; j++) {
319 ret[i][j] = j;
320 ret[i][mesh_size[i] - j] = -j;
321 }
323 ret[i][mesh_size[i] / 2] = 0;
324 }
325
326 return ret;
327}
328
329#if defined(ESPRESSO_P3M) or defined(ESPRESSO_DP3M)
332 bool UseR2C = false, unsigned int R2CDir = 2u>
334 /** @brief Data layout of the input real-space 3D matrix. */
336 std::conditional_t<RSpaceOrder == Utils::MemoryOrder::ROW_MAJOR,
337 Kokkos::LayoutRight, Kokkos::LayoutLeft>;
338 /** @brief Data layout of the output k-space 3D matrix. */
340 std::conditional_t<KSpaceOrder == Utils::MemoryOrder::ROW_MAJOR,
341 Kokkos::LayoutRight, Kokkos::LayoutLeft>;
342 /** @brief Data layout of the input real-space 3D matrix. */
343 static auto constexpr r_space_order = RSpaceOrder;
344 /** @brief Data layout of the output k-space 3D matrix. */
345 static auto constexpr k_space_order = KSpaceOrder;
346 /** @brief Use real-to-complex implementation. */
347 static auto constexpr use_r2c = UseR2C;
348 /** @brief Direction of the reduced dimension (if @c use_r2c is true). */
349 static auto constexpr r2c_dir = R2CDir;
350};
351#endif // defined(ESPRESSO_P3M) or defined(ESPRESSO_DP3M)
Vector implementation and trait types for boost qvm interoperability.
static DEVICE_QUALIFIER constexpr Vector< T, N > broadcast(typename Base::value_type const &value) noexcept
Create a vector that has all entries set to the same value.
Definition Vector.hpp:131
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
auto hadamard_division(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:402
auto hadamard_product(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:385
MemoryOrder
Definition index.hpp:33
Arch
P3M kernel architecture.
std::array< std::vector< int >, 3 > calc_p3m_mesh_shift(Utils::Vector3i const &mesh_size, bool zero_out_midpoint=false)
Calculate indices that shift P3MParameters::mesh by mesh/2.
void influence_function_r2c(auto &g_function, auto const &global_size, auto const &local_size, auto const &local_origin)
Adapt an influence function grid for real-to-complex FFTs.
auto constexpr P3M_EPSILON_METALLIC
This value indicates metallic boundary conditions.
std::conditional_t< RSpaceOrder==Utils::MemoryOrder::ROW_MAJOR, Kokkos::LayoutRight, Kokkos::LayoutLeft > r_space_layout
Data layout of the input real-space 3D matrix.
static auto constexpr r2c_dir
Direction of the reduced dimension (if use_r2c is true).
static auto constexpr use_r2c
Use real-to-complex implementation.
static auto constexpr r_space_order
Data layout of the input real-space 3D matrix.
static auto constexpr k_space_order
Data layout of the output k-space 3D matrix.
std::conditional_t< KSpaceOrder==Utils::MemoryOrder::ROW_MAJOR, Kokkos::LayoutRight, Kokkos::LayoutLeft > k_space_layout
Data layout of the output k-space 3D matrix.
Local mesh FFT buffers.
std::array< std::span< FloatType >, 3 > rs_fields
real-space vector meshes for the electric or dipolar field.
int ks_pnum
number of permutations in k_space
Utils::Vector3i start
Indices of the lower left corner of the local mesh grid.
std::span< FloatType > rs_scalar
real-space scalar mesh for charge assignment and FFT.
std::span< FloatType > rs_charge_density
real-space scalar charge density.
Utils::Vector3i stop
Indices of the upper right corner of the local mesh grid.
Utils::Vector3i size
Extents of the local mesh grid.
Properties of the local mesh and halo region.
Utils::Vector3i ur_no_halo
Utils::Vector3i ld_ind
index of lower left corner of the local mesh in the global mesh.
std::size_t size
number of local mesh points including halo layers.
Utils::Vector3i dim
dimension (size) of local mesh including halo layers.
void recalc_ld_pos(P3MParameters const &params)
Recalculate quantities derived from the mesh and box length: ld_pos (position of the left down mesh).
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:34
Utils::Vector3i dim_no_halo
Utils::Vector3i n_halo_ld
Utils::Vector3i inner
dimension of mesh inside node domain.
Utils::Vector3i in_ld
inner left down grid point
int r_margin[6]
number of margin mesh points from neighbour nodes
int margin[6]
number of margin mesh points.
int q_2_off
offset between mesh lines of the last dimension
Utils::Vector3i ld_no_halo
Utils::Vector3i in_ur
inner up right grid point + (1,1,1)
Utils::Vector3d ld_pos
position of the first local mesh point.
Utils::Vector3i n_halo_ur
int q_21_off
offset between mesh lines of the two last dimensions
Structure to hold P3M parameters and some dependent variables.
auto calc_grid_pos(Utils::Vector3d const &pos) const
Convert spatial position to grid position.
Utils::Vector3d cao_cut
cutoff for charge assignment.
double alpha
unscaled alpha_L for use with fast inline functions only
P3MParameters(bool tuning, double epsilon, double r_cut, Utils::Vector3i const &mesh, Utils::Vector3d const &mesh_off, int cao, double alpha, double accuracy)
double r_cut_iL
cutoff radius for real space electrostatics (>0), rescaled to r_cut_iL = r_cut * box_l_i.
int cao
charge assignment order ([0,7]).
double accuracy
accuracy of the actual parameter set.
double alpha_L
Ewald splitting parameter (0.
int cao3
number of points unto which a single charge is interpolated, i.e.
Utils::Vector3d mesh_off
offset of the first mesh point (lower left corner) from the coordinate origin ([0,...
Utils::Vector3d ai
inverse mesh constant.
double r_cut
unscaled r_cut_iL for use with fast inline functions only
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.
bool tuning
tuning or production?
Utils::Vector3i mesh
number of mesh points per coordinate direction (>0), in real space.
double epsilon
epsilon of the "surrounding dielectric".
Utils::Vector3d a
mesh constant.
std::pair< std::optional< int >, std::optional< int > > limits