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