ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
BoxGeometry.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2010-2026 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
24
25#include <utils/Vector.hpp>
26#include <utils/attributes.hpp>
27
28#include <bitset>
29#include <cassert>
30#include <cmath>
31#include <limits>
32#include <stdexcept>
33#include <utility>
34
35namespace detail {
36/**
37 * @brief Get the minimum-image distance between two coordinates.
38 *
39 * Branchless fold: the periodicity is encoded in the masked inverse box
40 * length (0 for non-periodic directions, where <tt>rint</tt> then yields a
41 * zero image shift). Uses <tt>rint</tt> (round half to even) rather than
42 * <tt>round</tt>, so it maps to a single rounding instruction; the two only
43 * differ for separations of exactly half a box length, where both images
44 * are equidistant.
45 *
46 * @param a Coordinate of the terminal point.
47 * @param b Coordinate of the initial point.
48 * @param box_length Box length.
49 * @param box_length_inv_masked Inverse box length if periodic, 0 otherwise.
50 * @return Shortest distance from @p b to @p a across periodic images,
51 * i.e. <tt>a - b</tt>. Can be negative.
52 */
53template <typename T>
54T get_mi_coord_masked(T a, T b, T box_length, T box_length_inv_masked) {
55 auto const dx = a - b;
56 return dx - std::rint(dx * box_length_inv_masked) * box_length;
57}
58
59/**
60 * @brief Get the minimum-image distance between two coordinates.
61 * @param a Coordinate of the terminal point.
62 * @param b Coordinate of the initial point.
63 * @param box_length Box length.
64 * @param periodic Box periodicity.
65 * @return Shortest distance from @p b to @p a across periodic images,
66 * i.e. <tt>a - b</tt>. Can be negative.
67 */
68template <typename T> T get_mi_coord(T a, T b, T box_length, bool periodic) {
69 return get_mi_coord_masked(a, b, box_length,
70 periodic ? T{1.} / box_length : T{0.});
71}
72
73/** @brief Calculate image box shift vector.
74 * @param image_box image box offset
75 * @param box box length
76 * @return Image box coordinates.
77 */
78inline auto image_shift(Utils::Vector3i const &image_box,
79 Utils::Vector3d const &box) {
80 return hadamard_product(image_box, box);
81}
82
83/** @brief Unfold particle coordinates to image box.
84 * @param pos coordinate to unfold
85 * @param image_box image box offset
86 * @param box box length
87 * @return Unfolded coordinates.
88 */
89inline auto unfolded_position(Utils::Vector3d const &pos,
90 Utils::Vector3i const &image_box,
91 Utils::Vector3d const &box) {
92 return pos + image_shift(image_box, box);
93}
94} // namespace detail
95
96enum class BoxType { CUBOID = 0, LEES_EDWARDS = 1 };
97
98/**
99 * @brief Cuboid minimum-image fold parameters for hot pair loops.
100 *
101 * Capture an instance by value in a kernel to hoist the box data out of the
102 * pair loop: member loads then come from the kernel's own frame and the
103 * compiler can keep them in registers, instead of re-reading them through a
104 * @ref BoxGeometry reference for every pair. Only valid for cuboid boxes;
105 * Lees-Edwards boxes need the full @ref BoxGeometry::get_mi_vector.
106 */
108 Utils::Vector3d m_length;
109 Utils::Vector3d m_length_inv_masked;
110
111public:
114 : m_length(length), m_length_inv_masked(length_inv_masked) {}
115
116 /** @brief Squared minimum-image distance between two coordinates. */
117 ESPRESSO_ATTR_ALWAYS_INLINE inline double
118 dist2(Utils::Vector3d const &a, Utils::Vector3d const &b) const {
119 double acc = 0.;
120 for (auto c = 0u; c < 3u; ++c) {
121 auto const dx = detail::get_mi_coord_masked(a[c], b[c], m_length[c],
122 m_length_inv_masked[c]);
123 acc += dx * dx;
124 }
125 return acc;
126 }
127
128 /**
129 * @brief Batched minimum-image vector and squared distance: one point
130 * (@p xi, @p yi, @p zi) against @p m others held in the SoA arrays
131 * @p sx / @p sy / @p sz.
132 *
133 * Writes the fold vector components to @p dx0 / @p dx1 / @p dx2 and the
134 * squared distance to @p dsq. The loop carries no dependency across the
135 * @p m entries and reads contiguous arrays, so it vectorizes. Each entry is
136 * computed as three per-axis @c detail::get_mi_coord_masked folds followed
137 * by `Utils::Vector::norm2` (accumulating from zero in component order), so
138 * the per-pair results are bitwise-identical to the scalar
139 * @ref BoxGeometry::get_mi_vector path for cuboid boxes.
140 */
142 batch_vector_dist2(double xi, double yi, double zi, int m, double const *sx,
143 double const *sy, double const *sz, double *dx0,
144 double *dx1, double *dx2, double *dsq) const {
145 auto const lx = m_length[0u];
146 auto const ly = m_length[1u];
147 auto const lz = m_length[2u];
148 auto const ix = m_length_inv_masked[0u];
149 auto const iy = m_length_inv_masked[1u];
150 auto const iz = m_length_inv_masked[2u];
151 for (int t = 0; t < m; ++t) {
152 auto const a0 = detail::get_mi_coord_masked(xi, sx[t], lx, ix);
153 auto const a1 = detail::get_mi_coord_masked(yi, sy[t], ly, iy);
154 auto const a2 = detail::get_mi_coord_masked(zi, sz[t], lz, iz);
155 dx0[t] = a0;
156 dx1[t] = a1;
157 dx2[t] = a2;
158 auto acc = 0.;
159 acc += a0 * a0;
160 acc += a1 * a1;
161 acc += a2 * a2;
162 dsq[t] = acc;
163 }
164 }
165};
166
168public:
170 set_length(Utils::Vector3d{1., 1., 1.});
171 set_periodic(0u, true);
172 set_periodic(1u, true);
173 set_periodic(2u, true);
175 }
177 m_type = rhs.type();
178 set_length(rhs.length());
179 set_periodic(0u, rhs.periodic(0u));
180 set_periodic(1u, rhs.periodic(1u));
181 set_periodic(2u, rhs.periodic(2u));
182 m_lees_edwards_bc = rhs.m_lees_edwards_bc;
183 }
184
185private:
186 BoxType m_type = BoxType::CUBOID;
187 /** Flags for all three dimensions whether pbc are applied (default). */
188 std::bitset<3> m_periodic = 0b111;
189 /** Side lengths of the box */
190 Utils::Vector3d m_length = {1., 1., 1.};
191 /** Inverse side lengths of the box */
192 Utils::Vector3d m_length_inv = {1., 1., 1.};
193 /** Inverse side lengths for periodic directions, 0 for non-periodic ones.
194 * Folding the periodicity into the inverse length makes the cuboid
195 * minimum-image fold branchless (see `detail::get_mi_coord_masked`). */
196 Utils::Vector3d m_length_inv_masked = {1., 1., 1.};
197 /** Half side lengths of the box */
198 Utils::Vector3d m_length_half = {0.5, 0.5, 0.5};
199
200 /** Lees-Edwards boundary conditions */
201 LeesEdwardsBC m_lees_edwards_bc;
202
203public:
204 /**
205 * @brief Set periodicity for direction
206 *
207 * @param coord The coordinate to set the periodicity for.
208 * @param val True if this direction should be periodic.
209 */
210 void set_periodic(unsigned coord, bool val) {
211 m_periodic.set(coord, val);
212 m_length_inv_masked[coord] = val ? m_length_inv[coord] : 0.;
213 }
214
215 /**
216 * @brief Check periodicity in direction.
217 *
218 * @param coord Direction to check
219 * @return true iff periodic in direction.
220 */
221 constexpr bool periodic(unsigned coord) const {
222 assert(coord <= 2u);
223 return m_periodic[coord];
224 }
225
226 /**
227 * @brief Box length
228 * @return Return vector of side-lengths of the box.
229 */
230 Utils::Vector3d const &length() const { return m_length; }
231
232 /**
233 * @brief Inverse box length
234 * @return Return vector of inverse side-lengths of the box.
235 */
236 Utils::Vector3d const &length_inv() const { return m_length_inv; }
237
238 /**
239 * @brief Half box length
240 * @return Return vector of half side-lengths of the box.
241 */
242 Utils::Vector3d const &length_half() const { return m_length_half; }
243
244 /**
245 * @brief Set box side lengths.
246 * @param box_l Length that should be set.
247 */
250 m_length = box_l;
251 m_length_inv = {1. / box_l[0], 1. / box_l[1], 1. / box_l[2]};
252 for (auto c = 0u; c < 3u; ++c) {
253 m_length_inv_masked[c] = m_periodic[c] ? m_length_inv[c] : 0.;
254 }
255 m_length_half = 0.5 * box_l;
256 }
257
258 /**
259 * @brief Box volume
260 * @return Return the volume of the box.
261 */
262 double volume() const { return Utils::product(m_length); }
263
264 /**
265 * @brief Get the minimum-image distance between two coordinates.
266 * @param a Coordinate of the terminal point.
267 * @param b Coordinate of the initial point.
268 * @param coord Direction
269 * @return Shortest distance from @p b to @p a across periodic images,
270 * i.e. <tt>a - b</tt>. Can be negative.
271 */
272 template <typename T> T inline get_mi_coord(T a, T b, unsigned coord) const {
273 assert(coord <= 2u);
274
275 return detail::get_mi_coord_masked(
276 a, b, static_cast<T>(m_length[coord]),
277 static_cast<T>(m_length_inv_masked[coord]));
278 }
279
280 /** @brief Cuboid minimum-image fold parameters for hoisting into kernels. */
281 auto cuboid_minimum_image() const {
282 return CuboidMinimumImage{m_length, m_length_inv_masked};
283 }
284
285 /**
286 * @brief Get the minimum-image vector between two coordinates.
287 *
288 * @tparam T Floating point type.
289 *
290 * @param a Coordinate of the terminal point.
291 * @param b Coordinate of the initial point.
292 * @return Vector from @p b to @p a that minimizes the distance across
293 * periodic images, i.e. <tt>a - b</tt>.
294 */
295 template <typename T>
298 if (type() == BoxType::LEES_EDWARDS) {
299 auto const shear_plane_normal = lees_edwards_bc().shear_plane_normal;
300 auto a_tmp = a;
301 auto b_tmp = b;
302 a_tmp[shear_plane_normal] = Algorithm::periodic_fold(
303 a_tmp[shear_plane_normal], m_length[shear_plane_normal]);
304 b_tmp[shear_plane_normal] = Algorithm::periodic_fold(
305 b_tmp[shear_plane_normal], m_length[shear_plane_normal]);
306 return lees_edwards_bc().distance(a_tmp - b_tmp, m_length, m_length_half,
307 m_length_inv, m_periodic);
308 }
310 return {get_mi_coord(a[0], b[0], 0u), get_mi_coord(a[1], b[1], 1u),
311 get_mi_coord(a[2], b[2], 2u)};
312 }
313
314 /**
315 * @brief Get the squared minimum-image distance between two coordinates.
316 *
317 * Equivalent to <tt>get_mi_vector(a, b).norm2()</tt>, but for cuboid
318 * boxes avoids constructing the intermediate vector.
319 *
320 * @tparam T Floating point type.
321 *
322 * @param a Coordinate of the terminal point.
323 * @param b Coordinate of the initial point.
324 * @return Squared shortest distance from @p b to @p a across periodic
325 * images.
326 */
327 template <typename T>
330 if (type() == BoxType::LEES_EDWARDS) {
331 return get_mi_vector(a, b).norm2();
332 }
334 auto const d0 = get_mi_coord(a[0], b[0], 0u);
335 auto const d1 = get_mi_coord(a[1], b[1], 1u);
336 auto const d2 = get_mi_coord(a[2], b[2], 2u);
337 return d0 * d0 + d1 * d1 + d2 * d2;
338 }
339
340 /**
341 * @brief Get the minimum-image vector between two coordinates.
342 *
343 * @tparam T Floating point type.
344 *
345 * @param a0 x element of the terminal point.
346 * @param a1 y element of the terminal point.
347 * @param a2 z element of the terminal point.
348 * @param b0 x element of the initial point.
349 * @param b1 y element of the initial point.
350 * @param b2 z element of the initial point.
351 * @return Vector from @p b to @p a that minimizes the distance across
352 * periodic images, i.e. <tt>a - b</tt>.
353 */
354 template <typename T>
356 get_mi_vector(T const &a0, T const &a1, T const &a2, T const &b0, T const &b1,
357 T const &b2) const {
358 if (type() == BoxType::LEES_EDWARDS) {
359 auto const shear_plane_normal = lees_edwards_bc().shear_plane_normal;
360 auto a_tmp = Utils::Vector3<T>{a0, a1, a2};
361 auto b_tmp = Utils::Vector3<T>{b0, b1, b2};
362 a_tmp[shear_plane_normal] = Algorithm::periodic_fold(
363 a_tmp[shear_plane_normal], m_length[shear_plane_normal]);
364 b_tmp[shear_plane_normal] = Algorithm::periodic_fold(
365 b_tmp[shear_plane_normal], m_length[shear_plane_normal]);
366 return lees_edwards_bc().distance(a_tmp - b_tmp, m_length, m_length_half,
367 m_length_inv, m_periodic);
368 }
370 return {get_mi_coord(a0, b0, 0u), get_mi_coord(a1, b1, 1u),
371 get_mi_coord(a2, b2, 2u)};
372 }
373
374 BoxType type() const { return m_type; }
375 void set_type(BoxType type) { m_type = type; }
376
377 LeesEdwardsBC const &lees_edwards_bc() const { return m_lees_edwards_bc; }
378 void set_lees_edwards_bc(LeesEdwardsBC bc) { m_lees_edwards_bc = bc; }
379
380 /**
381 * @brief Update the Lees-Edwards parameters of the box geometry
382 * for the current simulation time.
383 */
384 void lees_edwards_update(double pos_offset, double shear_velocity) {
386 m_lees_edwards_bc.pos_offset = pos_offset;
387 m_lees_edwards_bc.shear_velocity = shear_velocity;
388 }
389
390 /** Calculate the velocity difference including the Lees-Edwards velocity */
392 Utils::Vector3d const &y,
393 Utils::Vector3d const &u,
394 Utils::Vector3d const &v) const {
395 auto ret = u - v;
396 if (type() == BoxType::LEES_EDWARDS) {
397 auto const &le = m_lees_edwards_bc;
398 auto const shear_plane_normal = le.shear_plane_normal;
399 auto const shear_direction = le.shear_direction;
400 auto const dy = x[shear_plane_normal] - y[shear_plane_normal];
401 if (std::fabs(dy) > length_half()[shear_plane_normal]) {
402 ret[shear_direction] -= std::copysign(1.0, dy) * le.shear_velocity;
403 }
404 }
405 return ret;
406 }
407
408 /** @brief Fold coordinates to primary simulation box in-place.
409 * Lees-Edwards offset is ignored.
410 * @param[in,out] pos coordinates to fold
411 * @param[in,out] image_box image box offset
412 */
413 void fold_position(Utils::Vector3d &pos, Utils::Vector3i &image_box) const {
414 for (auto i = 0u; i < 3u; i++) {
415 if (m_periodic[i]) {
416 auto const result =
417 Algorithm::periodic_fold(pos[i], image_box[i], m_length[i]);
418 if (result.second == std::numeric_limits<int>::min() or
419 result.second == std::numeric_limits<int>::max()) {
420 throw std::runtime_error(
421 "Overflow in the image box count while folding a particle "
422 "coordinate into the primary simulation box. Maybe a particle "
423 "experienced a huge force.");
424 }
425 std::tie(pos[i], image_box[i]) = result;
426 }
427 }
428 }
429
430 /**
431 * @brief Calculate coordinates folded to primary simulation box.
432 * @param[in] pos coordinates to fold
433 * @return Folded coordinates.
434 */
435 auto folded_position(Utils::Vector3d const &pos) const {
436 auto pos_folded = pos;
437 for (auto i = 0u; i < 3u; i++) {
438 if (m_periodic[i]) {
439 pos_folded[i] = Algorithm::periodic_fold(pos[i], m_length[i]);
440 }
441 }
442
443 return pos_folded;
444 }
445
446 /**
447 * @brief Calculate image box of coordinates folded to primary simulation box.
448 * @param[in] pos coordinates
449 * @param[in] image_box image box to fold
450 * @return Folded image box.
451 */
453 Utils::Vector3i const &image_box) const {
454 auto image_box_folded = image_box;
455 for (auto i = 0u; i < 3u; i++) {
456 if (m_periodic[i]) {
458 Algorithm::periodic_fold(pos[i], image_box[i], m_length[i]).second;
459 }
460 }
461
462 return image_box_folded;
463 }
464
465 /** @brief Calculate image box shift vector */
466 auto image_shift(Utils::Vector3i const &image_box) const {
467 return detail::image_shift(image_box, m_length);
468 }
469
470 /** @brief Unfold particle coordinates to image box. */
472 Utils::Vector3i const &image_box) const {
473 return detail::unfolded_position(pos, image_box, m_length);
474 }
475};
BoxType
@ LEES_EDWARDS
static int coord(std::string const &s)
Vector implementation and trait types for boost qvm interoperability.
Compiler-attribute macros shared across ESPResSo headers.
#define ESPRESSO_ATTR_ALWAYS_INLINE
auto unfolded_position(Utils::Vector3d const &pos, Utils::Vector3i const &image_box) const
Unfold particle coordinates to image box.
T get_mi_coord(T a, T b, unsigned coord) const
Get the minimum-image distance between two coordinates.
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.
void lees_edwards_update(double pos_offset, double shear_velocity)
Update the Lees-Edwards parameters of the box geometry for the current simulation time.
auto folded_position(Utils::Vector3d const &pos) const
Calculate coordinates folded to primary simulation box.
Utils::Vector3d const & length() const
Box length.
LeesEdwardsBC const & lees_edwards_bc() const
BoxGeometry(BoxGeometry const &rhs)
constexpr bool periodic(unsigned coord) const
Check periodicity in direction.
ESPRESSO_ATTR_ALWAYS_INLINE T get_mi_dist2(Utils::Vector3< T > const &a, Utils::Vector3< T > const &b) const
Get the squared minimum-image distance between two coordinates.
auto cuboid_minimum_image() const
Cuboid minimum-image fold parameters for hoisting into kernels.
double volume() const
Box volume.
BoxType type() const
auto image_shift(Utils::Vector3i const &image_box) const
Calculate image box shift vector.
Utils::Vector3d const & length_half() const
Half box length.
Utils::Vector3d const & length_inv() const
Inverse box length.
void set_periodic(unsigned coord, bool val)
Set periodicity for direction.
void set_length(Utils::Vector3d const &box_l)
Set box side lengths.
auto folded_image_box(Utils::Vector3d const &pos, Utils::Vector3i const &image_box) const
Calculate image box of coordinates folded to primary simulation box.
void set_lees_edwards_bc(LeesEdwardsBC bc)
void fold_position(Utils::Vector3d &pos, Utils::Vector3i &image_box) const
Fold coordinates to primary simulation box in-place.
Utils::Vector3d velocity_difference(Utils::Vector3d const &x, Utils::Vector3d const &y, Utils::Vector3d const &u, Utils::Vector3d const &v) const
Calculate the velocity difference including the Lees-Edwards velocity.
void set_type(BoxType type)
ESPRESSO_ATTR_ALWAYS_INLINE Utils::Vector3< T > get_mi_vector(T const &a0, T const &a1, T const &a2, T const &b0, T const &b1, T const &b2) const
Get the minimum-image vector between two coordinates.
Cuboid minimum-image fold parameters for hot pair loops.
CuboidMinimumImage(Utils::Vector3d const &length, Utils::Vector3d const &length_inv_masked)
ESPRESSO_ATTR_ALWAYS_INLINE double dist2(Utils::Vector3d const &a, Utils::Vector3d const &b) const
Squared minimum-image distance between two coordinates.
ESPRESSO_ATTR_ALWAYS_INLINE void batch_vector_dist2(double xi, double yi, double zi, int m, double const *sx, double const *sy, double const *sz, double *dx0, double *dx1, double *dx2, double *dsq) const
Batched minimum-image vector and squared distance: one point (xi, yi, zi) against m others held in th...
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:132
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
auto periodic_fold(std::floating_point auto x, std::integral auto i, std::floating_point auto l)
Fold value into primary interval.
T product(Vector< T, N > const &v)
Definition Vector.hpp:383
auto hadamard_product(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:388
unsigned int shear_plane_normal
Utils::Vector3d distance(Utils::Vector3d const &d, Utils::Vector3d const &l, Utils::Vector3d const &, Utils::Vector3d const &l_inv, std::bitset< 3 > const periodic) const