ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
ghosts.cpp
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/** \file
22 * Ghost particles and particle exchange.
23 *
24 * For more information on ghosts,
25 * see \ref ghosts.hpp "ghosts.hpp"
26 *
27 * Note on variable naming:
28 * - a "GhostCommunicator" is always named "gcr",
29 * - a "GhostCommunication" is always named "ghost_comm".
30 */
31#include "ghosts.hpp"
32
33#include "BoxGeometry.hpp"
34#include "Particle.hpp"
35#include "system/System.hpp"
36
38
39#include <boost/archive/binary_iarchive.hpp>
40#include <boost/archive/binary_oarchive.hpp>
41#include <boost/iostreams/device/array.hpp>
42#include <boost/iostreams/device/back_inserter.hpp>
43#include <boost/iostreams/stream.hpp>
44#include <boost/mpi/collectives.hpp>
45#include <boost/range/numeric.hpp>
46#include <boost/serialization/vector.hpp>
47
48#include <algorithm>
49#include <cassert>
50#include <cstddef>
51#include <functional>
52#include <iterator>
53#include <limits>
54#include <span>
55#include <vector>
56
57/** Tag for ghosts communications. */
58#define REQ_GHOST_SEND 100
59
60/**
61 * Class that stores marshalled data for ghost communications.
62 * To store and retrieve data, use the adapter classes below.
63 */
64class CommBuf {
65public:
66 /** Returns a pointer to the non-bond storage.
67 */
68 char *data() { return buf.data(); }
69 const char *data() const { return buf.data(); }
70
71 /** Returns the number of elements in the non-bond storage.
72 */
73 std::size_t size() const { return buf.size(); }
74
75 /** Resizes the underlying storage s.t. the object is capable
76 * of holding "new_size" chars.
77 * @param new_size new size
78 */
79 void resize(std::size_t new_size) { buf.resize(new_size); }
80
81 /** Returns a reference to the bond storage.
82 */
83 auto &bonds() { return bondbuf; }
84 const auto &bonds() const { return bondbuf; }
85
86 auto make_span() { return std::span(buf.data(), buf.size()); }
87
88private:
89 std::vector<char> buf; ///< Buffer for everything but bonds
90 std::vector<char> bondbuf; ///< Buffer for bond lists
91};
92
93/** @brief Pseudo-archive to calculate the size of the serialization buffer. */
95 std::size_t m_size = 0;
96
97public:
98 auto size() const { return m_size; }
99
100 template <class T> auto &operator<<(T &) {
101 m_size += sizeof(T);
102 return *this;
103 }
104
105 template <class T> auto &operator&(T &t) { return *this << t; }
106};
107
108/** @brief Type of reduction to carry out during serialization. */
109enum class ReductionPolicy {
110 /** @brief Reduction for domain-to-domain particle communication. */
111 MOVE,
112 /** @brief Reduction for cell-to-cell particle update. */
113 UPDATE,
114};
115
116/** @brief Whether to save the state to or load the state from the archive. */
118
119/**
120 * @brief Serialize particle data, possibly with reduction.
121 * The reduction can take place during the save stage, e.g. to apply
122 * a ghost shift to the particle position, or during the load stage,
123 * e.g. to transfer momentum between particles in two local cells.
124 */
125template <class Archive>
126static void
128 ReductionPolicy policy, SerializationDirection direction,
129 BoxGeometry const &box_geo,
132 ar & p.id() & p.mol_id() & p.type() & p.propagation();
133#ifdef ESPRESSO_ROTATION
134 ar & p.rotation();
135#ifdef ESPRESSO_ROTATIONAL_INERTIA
136 ar & p.rinertia();
137#endif
138#endif
139#ifdef ESPRESSO_MASS
140 ar & p.mass();
141#endif
142#ifdef ESPRESSO_ELECTROSTATICS
143 ar & p.q();
144#endif
145#ifdef ESPRESSO_DIPOLES
146 ar & p.dipm();
147#endif
148#ifdef ESPRESSO_LB_ELECTROHYDRODYNAMICS
149 ar & p.mu_E();
150#endif
151#ifdef ESPRESSO_VIRTUAL_SITES_RELATIVE
152 ar & p.vs_relative();
153#endif
154#ifdef ESPRESSO_THERMOSTAT_PER_PARTICLE
155 ar & p.gamma();
156#ifdef ESPRESSO_ROTATION
157 ar & p.gamma_rot();
158#endif
159#endif
160#ifdef ESPRESSO_EXTERNAL_FORCES
161 ar & p.fixed();
162 ar & p.ext_force();
163#ifdef ESPRESSO_ROTATION
164 ar & p.ext_torque();
165#endif
166#endif
167#ifdef ESPRESSO_ENGINE
168 ar & p.swimming();
169#endif
170 }
172 if (direction == SerializationDirection::SAVE and ghost_shift != nullptr) {
173 /* ok, this is not nice, but perhaps fast */
174 auto pos = p.pos() + *ghost_shift;
175 auto img = p.image_box();
176 box_geo.fold_position(pos, img);
177 ar & pos;
178 ar & img;
179 } else {
180 ar & p.pos();
181 ar & p.image_box();
182 }
183#ifdef ESPRESSO_ROTATION
184 ar & p.quat();
185#endif
186#ifdef ESPRESSO_BOND_CONSTRAINT
188#endif
189 }
191 ar & p.v();
192#ifdef ESPRESSO_ROTATION
193 ar & p.omega();
194#endif
195 }
197 if (policy == ReductionPolicy::UPDATE and
198 direction == SerializationDirection::LOAD) {
199 Utils::Vector3d force;
200 ar & force;
201 p.force() += force;
202 } else {
203 ar & p.force();
204 }
205#ifdef ESPRESSO_ROTATION
206 if (policy == ReductionPolicy::UPDATE and
207 direction == SerializationDirection::LOAD) {
208 Utils::Vector3d torque;
209 ar & torque;
210 p.torque() += torque;
211 } else {
212 ar & p.torque();
213 }
214#endif
215 }
216#ifdef ESPRESSO_BOND_CONSTRAINT
218 if (policy == ReductionPolicy::UPDATE and
219 direction == SerializationDirection::LOAD) {
220 Utils::Vector3d correction;
221 ar & correction;
222 p.rattle_correction() += correction;
223 } else {
224 ar & p.rattle_correction();
225 }
226 }
227#endif
228}
229
238
240 BoxGeometry const &box_geo,
241 unsigned int data_parts) {
243 return sizeof(unsigned int) * ghost_comm.part_lists.size();
244
245 auto const n_part = boost::accumulate(
246 ghost_comm.part_lists, std::size_t{0},
247 [](std::size_t sum, auto part_list) { return sum + part_list->size(); });
248
249 return n_part * calc_transmit_size(box_geo, data_parts);
250}
251
254 BoxGeometry const &box_geo,
255 unsigned int data_parts) {
256
257 /* reallocate send buffer */
259 send_buffer.bonds().clear();
260
261 auto archiver = Utils::MemcpyOArchive{send_buffer.make_span()};
262
263 /* Construct archive that pushes back to the bond buffer */
264 namespace io = boost::iostreams;
265 io::stream<io::back_insert_device<std::vector<char>>> os{
266 io::back_inserter(send_buffer.bonds())};
267 boost::archive::binary_oarchive bond_archiver{os};
268
269 /* put in data */
270 for (auto part_list : ghost_comm.part_lists) {
272 assert(part_list->size() <= std::numeric_limits<unsigned int>::max());
273 auto np = static_cast<unsigned int>(part_list->size());
274 archiver << np;
275 } else {
276 for (auto &p : *part_list) {
279 &ghost_comm.shift);
281 bond_archiver << p.bonds();
282 }
283 }
284 }
285 }
286
287 assert(archiver.bytes_written() == send_buffer.size());
288}
289
290static void prepare_ghost_cell(ParticleList *cell, std::size_t size) {
291 /* Adapt size */
292 cell->resize(size);
293
294 /* Mark particles as ghosts */
295 for (auto &p : *cell) {
296 p.set_ghost(true);
297 }
298}
299
302 BoxGeometry const &box_geo,
303 unsigned int data_parts) {
304 /* reallocate recv buffer */
306 /* clear bond buffer */
307 recv_buffer.bonds().clear();
308}
309
312 BoxGeometry const &box_geo,
313 unsigned int data_parts) {
314 /* put back data */
315 auto archiver = Utils::MemcpyIArchive{recv_buffer.make_span()};
316
318 for (auto part_list : ghost_comm.part_lists) {
319 unsigned int np;
320 archiver >> np;
322 }
323 } else {
324 for (auto part_list : ghost_comm.part_lists) {
325 for (auto &p : *part_list) {
327 SerializationDirection::LOAD, box_geo, nullptr);
328 }
329 }
331 namespace io = boost::iostreams;
332 io::stream<io::array_source> bond_stream(io::array_source{
333 recv_buffer.bonds().data(), recv_buffer.bonds().size()});
334 boost::archive::binary_iarchive bond_archiver(bond_stream);
335
336 for (auto part_list : ghost_comm.part_lists) {
337 for (auto &p : *part_list) {
338 bond_archiver >> p.bonds();
339 }
340 }
341 }
342 }
343
344 assert(archiver.bytes_read() == recv_buffer.size());
345
346 recv_buffer.bonds().clear();
347}
348
349#ifdef ESPRESSO_BOND_CONSTRAINT
350static void
353 /* put back data */
354 auto archiver = Utils::MemcpyIArchive{recv_buffer.make_span()};
355 for (auto &part_list : ghost_comm.part_lists) {
356 for (Particle &part : *part_list) {
358 archiver >> pr;
359 part.rattle_params() += pr;
360 }
361 }
362}
363#endif
364
367 /* put back data */
368 auto archiver = Utils::MemcpyIArchive{recv_buffer.make_span()};
369 for (auto &part_list : ghost_comm.part_lists) {
370 for (Particle &part : *part_list) {
372 archiver >> pf;
373 part.force_and_torque() += pf;
374 }
375 }
376}
377
379 BoxGeometry const &box_geo,
380 unsigned int data_parts) {
381 CommBuf buffer;
383 buffer.resize(calc_transmit_size(box_geo, data_parts));
384 }
385 /* transfer data */
386 auto const offset = ghost_comm.part_lists.size() / 2;
387 for (std::size_t pl = 0; pl < offset; pl++) {
388 auto *src_list = ghost_comm.part_lists[pl];
389 auto *dst_list = ghost_comm.part_lists[pl + offset];
390
393 } else {
394 auto &src_part = *src_list;
395 auto &dst_part = *dst_list;
396 assert(src_part.size() == dst_part.size());
397
398 for (std::size_t i = 0; i < src_part.size(); i++) {
399 auto ar_out = Utils::MemcpyOArchive{buffer.make_span()};
400 auto ar_in = Utils::MemcpyIArchive{buffer.make_span()};
401 auto &p1 = src_part.begin()[i];
402 auto &p2 = dst_part.begin()[i];
405 &ghost_comm.shift);
407 SerializationDirection::LOAD, box_geo, nullptr);
409 p2.bonds() = p1.bonds();
410 }
411 }
412 }
413 }
414}
415
416static bool is_send_op(int comm_type, int node, int this_node) {
417 return ((comm_type == GHOST_SEND) || (comm_type == GHOST_RDCE) ||
418 (comm_type == GHOST_BCST && node == this_node));
419}
420
421static bool is_recv_op(int comm_type, int node, int this_node) {
422 return ((comm_type == GHOST_RECV) ||
423 (comm_type == GHOST_BCST && node != this_node) ||
424 (comm_type == GHOST_RDCE && node == this_node));
425}
426
428 int this_node) {
429 int const comm_type = ghost_comm.type & GHOST_JOBMASK;
430 int const prefetch = ghost_comm.type & GHOST_PREFETCH;
431 int const node = ghost_comm.node;
432 return is_send_op(comm_type, node, this_node) && prefetch;
433}
434
436 int this_node) {
437 int const comm_type = ghost_comm.type & GHOST_JOBMASK;
438 int const poststore = ghost_comm.type & GHOST_PSTSTORE;
439 int const node = ghost_comm.node;
440 return is_recv_op(comm_type, node, this_node) && poststore;
441}
442
444 BoxGeometry const &box_geo, unsigned int data_parts) {
446 return;
447
449
450 auto const &comm = gcr.mpi_comm;
451
452 for (auto cit = gcr.communications.cbegin(); cit != gcr.communications.cend();
453 ++cit) {
454 auto const &ghost_comm = *cit;
455 int const comm_type = ghost_comm.type & GHOST_JOBMASK;
456
457 if (comm_type == GHOST_LOCL) {
459 continue;
460 }
461
462 int const prefetch = ghost_comm.type & GHOST_PREFETCH;
463 int const poststore = ghost_comm.type & GHOST_PSTSTORE;
464 int const node = ghost_comm.node;
465
466 /* prepare send buffer if necessary */
467 if (is_send_op(comm_type, node, comm.rank())) {
468 /* ok, we send this step, prepare send buffer if not yet done */
469 if (!prefetch) {
471 }
472 // Check prefetched send buffers (must also hold for buffers allocated
473 // in the previous lines.)
474 assert(send_buffer.size() ==
476 } else if (prefetch) {
477 /* we do not send this time, let's look for a prefetch */
479 std::find_if(std::next(cit), gcr.communications.cend(),
480 [this_node = comm.rank()](auto const &other_ghost_comm) {
481 return is_prefetchable(other_ghost_comm, this_node);
482 });
483
484 if (prefetch_ghost_comm != gcr.communications.end())
486 data_parts);
487 }
488
489 /* recv buffer for recv and multinode operations to this node */
490 if (is_recv_op(comm_type, node, comm.rank()))
492
493 /* transfer data */
494 // Use two send/recvs in order to avoid having to serialize CommBuf
495 // (which consists of already serialized data).
496 switch (comm_type) {
497 case GHOST_RECV:
498 comm.recv(node, REQ_GHOST_SEND, recv_buffer.data(),
499 static_cast<int>(recv_buffer.size()));
500 comm.recv(node, REQ_GHOST_SEND, recv_buffer.bonds());
501 break;
502 case GHOST_SEND:
503 comm.send(node, REQ_GHOST_SEND, send_buffer.data(),
504 static_cast<int>(send_buffer.size()));
505 comm.send(node, REQ_GHOST_SEND, send_buffer.bonds());
506 break;
507 case GHOST_BCST:
508 if (node == comm.rank()) {
509 boost::mpi::broadcast(comm, send_buffer.data(),
510 static_cast<int>(send_buffer.size()), node);
511 boost::mpi::broadcast(comm, send_buffer.bonds(), node);
512 } else {
513 boost::mpi::broadcast(comm, recv_buffer.data(),
514 static_cast<int>(recv_buffer.size()), node);
515 boost::mpi::broadcast(comm, recv_buffer.bonds(), node);
516 }
517 break;
518 case GHOST_RDCE:
519 if (node == comm.rank())
520 boost::mpi::reduce(
521 comm, reinterpret_cast<double *>(send_buffer.data()),
522 static_cast<int>(send_buffer.size() / sizeof(double)),
523 reinterpret_cast<double *>(recv_buffer.data()), std::plus<double>{},
524 node);
525 else
526 boost::mpi::reduce(
527 comm, reinterpret_cast<double *>(send_buffer.data()),
528 static_cast<int>(send_buffer.size() / sizeof(double)),
529 std::plus<double>{}, node);
530 break;
531 }
532
533 // recv op; write back data directly, if no PSTSTORE delay is requested.
534 if (is_recv_op(comm_type, node, comm.rank())) {
535 if (!poststore) {
536 /* forces have to be added, the rest overwritten. Exception is RDCE,
537 * where the addition is integrated into the communication. */
540#ifdef ESPRESSO_BOND_CONSTRAINT
543#endif
544 else
546 }
547 } else if (poststore) {
548 /* send op; write back delayed data from last recv, when this was a
549 * prefetch send. */
550 /* find previous action where we recv and which has PSTSTORE set */
551 auto poststore_ghost_comm = std::find_if(
552 std::make_reverse_iterator(cit), gcr.communications.crend(),
553 [this_node = comm.rank()](auto const &other_ghost_comm) {
554 return is_poststorable(other_ghost_comm, this_node);
555 });
556
557 if (poststore_ghost_comm != gcr.communications.rend()) {
558 assert(recv_buffer.size() ==
560 /* as above */
563#ifdef ESPRESSO_BOND_CONSTRAINT
567#endif
568 else
570 data_parts);
571 }
572 }
573 }
574}
void fold_position(Utils::Vector3d &pos, Utils::Vector3i &image_box) const
Fold coordinates to primary simulation box in-place.
Class that stores marshalled data for ghost communications.
Definition ghosts.cpp:64
const auto & bonds() const
Definition ghosts.cpp:84
const char * data() const
Definition ghosts.cpp:69
char * data()
Returns a pointer to the non-bond storage.
Definition ghosts.cpp:68
auto make_span()
Definition ghosts.cpp:86
std::size_t size() const
Returns the number of elements in the non-bond storage.
Definition ghosts.cpp:73
auto & bonds()
Returns a reference to the bond storage.
Definition ghosts.cpp:83
void resize(std::size_t new_size)
Resizes the underlying storage s.t.
Definition ghosts.cpp:79
Pseudo-archive to calculate the size of the serialization buffer.
Definition ghosts.cpp:94
void resize(std::size_t new_size)
Resize container.
Definition Bag.hpp:129
Archive that deserializes from a buffer via memcpy.
Archive that serializes to a buffer via memcpy.
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
int this_node
The number of this node.
static void serialize_and_reduce(Archive &ar, Particle &p, unsigned int data_parts, ReductionPolicy policy, SerializationDirection direction, BoxGeometry const &box_geo, Utils::Vector3d const *ghost_shift)
Serialize particle data, possibly with reduction.
Definition ghosts.cpp:127
static void add_rattle_correction_from_recv_buffer(CommBuf &recv_buffer, const GhostCommunication &ghost_comm)
Definition ghosts.cpp:351
static void prepare_recv_buffer(CommBuf &recv_buffer, GhostCommunication const &ghost_comm, BoxGeometry const &box_geo, unsigned int data_parts)
Definition ghosts.cpp:300
#define REQ_GHOST_SEND
Tag for ghosts communications.
Definition ghosts.cpp:58
static void put_recv_buffer(CommBuf &recv_buffer, GhostCommunication const &ghost_comm, BoxGeometry const &box_geo, unsigned int data_parts)
Definition ghosts.cpp:310
static void prepare_send_buffer(CommBuf &send_buffer, GhostCommunication const &ghost_comm, BoxGeometry const &box_geo, unsigned int data_parts)
Definition ghosts.cpp:252
ReductionPolicy
Type of reduction to carry out during serialization.
Definition ghosts.cpp:109
@ UPDATE
Reduction for cell-to-cell particle update.
@ MOVE
Reduction for domain-to-domain particle communication.
static bool is_recv_op(int comm_type, int node, int this_node)
Definition ghosts.cpp:421
static void add_forces_from_recv_buffer(CommBuf &recv_buffer, const GhostCommunication &ghost_comm)
Definition ghosts.cpp:365
static void cell_cell_transfer(GhostCommunication const &ghost_comm, BoxGeometry const &box_geo, unsigned int data_parts)
Definition ghosts.cpp:378
static bool is_send_op(int comm_type, int node, int this_node)
Definition ghosts.cpp:416
static void prepare_ghost_cell(ParticleList *cell, std::size_t size)
Definition ghosts.cpp:290
static auto calc_transmit_size(BoxGeometry const &box_geo, unsigned data_parts)
Definition ghosts.cpp:230
static bool is_prefetchable(GhostCommunication const &ghost_comm, int this_node)
Definition ghosts.cpp:427
void ghost_communicator(GhostCommunicator const &gcr, BoxGeometry const &box_geo, unsigned int data_parts)
Do a ghost communication with the specified data parts.
Definition ghosts.cpp:443
static bool is_poststorable(GhostCommunication const &ghost_comm, int this_node)
Definition ghosts.cpp:435
SerializationDirection
Whether to save the state to or load the state from the archive.
Definition ghosts.cpp:117
Ghost particles and particle exchange.
#define GHOST_RECV
recv from a single node
Definition ghosts.hpp:108
#define GHOST_PSTSTORE
additional flag for poststoring
Definition ghosts.hpp:121
#define GHOST_JOBMASK
mask to the job area of the transfer type
Definition ghosts.hpp:117
#define GHOST_RDCE
reduce, the node entry gives the receiver
Definition ghosts.hpp:112
#define GHOST_LOCL
transfer data from cell to cell on this node
Definition ghosts.hpp:114
#define GHOST_BCST
broadcast, the node entry gives the sender
Definition ghosts.hpp:110
@ GHOSTTRANS_MOMENTUM
transfer ParticleMomentum
Definition ghosts.hpp:132
@ GHOSTTRANS_RATTLE
transfer ParticleRattle
Definition ghosts.hpp:137
@ GHOSTTRANS_PARTNUM
resize the receiver particle arrays to the size of the senders
Definition ghosts.hpp:140
@ GHOSTTRANS_POSITION
transfer ParticlePosition
Definition ghosts.hpp:130
@ GHOSTTRANS_PROPRTS
transfer ParticleProperties
Definition ghosts.hpp:128
@ GHOSTTRANS_FORCE
transfer ParticleForce
Definition ghosts.hpp:134
@ GHOSTTRANS_NONE
Definition ghosts.hpp:126
@ GHOSTTRANS_BONDS
Definition ghosts.hpp:141
#define GHOST_SEND
send to a single node
Definition ghosts.hpp:106
#define GHOST_PREFETCH
additional flag for prefetching
Definition ghosts.hpp:119
Properties for a ghost communication.
Definition ghosts.hpp:159
Force information on a particle.
Definition Particle.hpp:330
Struct holding all information for one particle.
Definition Particle.hpp:435
auto const & swimming() const
Definition Particle.hpp:633
auto const & propagation() const
Definition Particle.hpp:461
auto const & rinertia() const
Definition Particle.hpp:572
auto const & mass() const
Definition Particle.hpp:492
auto const & quat() const
Definition Particle.hpp:517
auto const & rotation() const
Definition Particle.hpp:498
auto const & vs_relative() const
Definition Particle.hpp:599
auto const & q() const
Definition Particle.hpp:578
auto const & gamma() const
Definition Particle.hpp:603
auto const & pos_last_time_step() const
Definition Particle.hpp:637
auto const & gamma_rot() const
Definition Particle.hpp:606
auto const & v() const
Definition Particle.hpp:473
auto const & torque() const
Definition Particle.hpp:519
auto const & fixed() const
Definition Particle.hpp:611
auto const & ext_force() const
Definition Particle.hpp:626
auto const & omega() const
Definition Particle.hpp:521
auto const & image_box() const
Definition Particle.hpp:484
auto const & type() const
Definition Particle.hpp:458
auto const & ext_torque() const
Definition Particle.hpp:524
auto const & dipm() const
Definition Particle.hpp:533
auto const & mol_id() const
Definition Particle.hpp:456
auto const & pos() const
Definition Particle.hpp:471
auto const & mu_E() const
Definition Particle.hpp:584
auto const & force() const
Definition Particle.hpp:475
auto const & id() const
Definition Particle.hpp:454
auto const & rattle_correction() const
Definition Particle.hpp:641