ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
particle_packing.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 * Reusable particle packing/unpacking for ghost communications.
23 */
24
25#include "particle_packing.hpp"
26
27#include "BoxGeometry.hpp"
28#include "Particle.hpp"
29#include "ghosts.hpp"
30
32
33#include <boost/archive/binary_iarchive.hpp>
34#include <boost/archive/binary_oarchive.hpp>
35#include <boost/iostreams/device/array.hpp>
36#include <boost/iostreams/device/back_inserter.hpp>
37#include <boost/iostreams/stream.hpp>
38#include <boost/serialization/vector.hpp>
39
40#include <algorithm>
41#include <cassert>
42#include <cstddef>
43#include <iterator>
44#include <limits>
45#include <numeric>
46#include <span>
47#include <vector>
48
49namespace GhostComm {
50
51/** @brief Pseudo-archive to calculate the size of the serialization buffer. */
53 std::size_t m_size = 0;
54
55public:
56 auto size() const { return m_size; }
57
58 template <class T> auto &operator<<(T &) {
59 m_size += sizeof(T);
60 return *this;
61 }
62
63 template <class T> auto &operator&(T &t) { return *this << t; }
64};
65
66/** @brief Type of reduction to carry out during serialization. */
67enum class ReductionPolicy {
68 /** @brief Reduction for domain-to-domain particle communication. */
69 MOVE,
70 /** @brief Reduction for cell-to-cell particle update. */
71 UPDATE,
72};
73
74/** @brief Whether to save the state to or load the state from the archive. */
76
77/**
78 * @brief Serialize particle data, possibly with reduction.
79 * The reduction can take place during the save stage, e.g. to apply
80 * a ghost shift to the particle position, or during the load stage,
81 * e.g. to transfer momentum between particles in two local cells.
82 */
83template <class Archive>
84static void
85serialize_and_reduce(Archive &ar, Particle &p, unsigned int data_parts,
87 BoxGeometry const &box_geo,
88 Utils::Vector3d const *ghost_shift) {
89 if (data_parts & GHOSTTRANS_PROPRTS) {
90 ar & p.id() & p.mol_id() & p.type() & p.propagation();
91#ifdef ESPRESSO_ROTATION
92 ar & p.rotation();
93#ifdef ESPRESSO_ROTATIONAL_INERTIA
94 ar & p.rinertia();
95#endif
96#endif
97#ifdef ESPRESSO_MASS
98 ar & p.mass();
99#endif
100#ifdef ESPRESSO_ELECTROSTATICS
101 ar & p.q();
102#endif
103#ifdef ESPRESSO_DIPOLES
104 ar & p.dipm();
105#endif
106#ifdef ESPRESSO_LB_ELECTROHYDRODYNAMICS
107 ar & p.mu_E();
108#endif
109#ifdef ESPRESSO_VIRTUAL_SITES_RELATIVE
110 ar & p.vs_relative();
111#endif
112#ifdef ESPRESSO_THERMOSTAT_PER_PARTICLE
113 ar & p.gamma();
114#ifdef ESPRESSO_ROTATION
115 ar & p.gamma_rot();
116#endif
117#endif
118#ifdef ESPRESSO_EXTERNAL_FORCES
119 ar & p.fixed();
120 ar & p.ext_force();
121#ifdef ESPRESSO_ROTATION
122 ar & p.ext_torque();
123#endif
124#endif
125#ifdef ESPRESSO_ENGINE
126 ar & p.swimming();
127#endif
128 }
129 if (data_parts & GHOSTTRANS_POSITION) {
130 if (direction == SerializationDirection::SAVE and ghost_shift != nullptr) {
131 /* ok, this is not nice, but perhaps fast */
132 auto pos = p.pos() + *ghost_shift;
133 auto img = p.image_box();
134 box_geo.fold_position(pos, img);
135 ar & pos;
136 ar & img;
137 } else {
138 ar & p.pos();
139 ar & p.image_box();
140 }
141#ifdef ESPRESSO_BOND_CONSTRAINT
142 ar & p.pos_last_time_step();
143#endif
144 }
145 // Wire-symmetry: pack and unpack use the same data_parts per exchange,
146 // so the layout is always symmetric; QUAT follows POSITION in the stream
147 // when both are set (position push), and TORQUE follows FORCE when both
148 // are set (force reduce).
149#ifdef ESPRESSO_ROTATION
150 if (data_parts & GHOSTTRANS_QUAT) {
151 ar & p.quat();
152 }
153#endif
154 if (data_parts & GHOSTTRANS_MOMENTUM) {
155 ar & p.v();
156#ifdef ESPRESSO_ROTATION
157 ar & p.omega();
158#endif
159 }
160 if (data_parts & GHOSTTRANS_FORCE) {
161 if (policy == ReductionPolicy::UPDATE and
162 direction == SerializationDirection::LOAD) {
163 Utils::Vector3d force;
164 ar & force;
165 p.force() += force;
166 } else {
167 ar & p.force();
168 }
169 }
170#ifdef ESPRESSO_ROTATION
171 if (data_parts & GHOSTTRANS_TORQUE) {
172 if (policy == ReductionPolicy::UPDATE and
173 direction == SerializationDirection::LOAD) {
174 Utils::Vector3d torque;
175 ar & torque;
176 p.torque() += torque;
177 } else {
178 ar & p.torque();
179 }
180 }
181#endif
182#ifdef ESPRESSO_BOND_CONSTRAINT
183 if (data_parts & GHOSTTRANS_RATTLE) {
184 if (policy == ReductionPolicy::UPDATE and
185 direction == SerializationDirection::LOAD) {
186 Utils::Vector3d correction;
187 ar & correction;
188 p.rattle_correction() += correction;
189 } else {
190 ar & p.rattle_correction();
191 }
192 }
193#endif
194#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
195 if (data_parts & GHOSTTRANS_DIPFLD) {
196 if (policy == ReductionPolicy::UPDATE and
197 direction == SerializationDirection::LOAD) {
198 Utils::Vector3d dip_fld;
199 ar & dip_fld;
200 p.dip_fld() += dip_fld;
201 } else {
202 ar & p.dip_fld();
203 }
204 }
205#endif
206}
207
208static void prepare_ghost_cell(ParticleList *cell, std::size_t size) {
209 /* Adapt size */
210 cell->resize(size);
211
212 /* Mark particles as ghosts */
213 for (auto &p : *cell) {
214 p.set_ghost(true);
215 }
216}
217
218std::size_t calc_transmit_size(BoxGeometry const &box_geo,
219 unsigned data_parts) {
220 SerializationSizeCalculator sizeof_archive;
221 Particle p{};
222 serialize_and_reduce(sizeof_archive, p, data_parts, ReductionPolicy::MOVE,
223 SerializationDirection::SAVE, box_geo, nullptr);
224 return sizeof_archive.size();
225}
226
227std::size_t calc_transmit_size(std::span<ParticleList *const> cells,
228 BoxGeometry const &box_geo,
229 unsigned data_parts) {
230 if (data_parts & GHOSTTRANS_PARTNUM)
231 return sizeof(unsigned int) * cells.size();
232
233 auto const n_part = std::accumulate(
234 cells.begin(), cells.end(), std::size_t{0},
235 [](std::size_t sum, auto part_list) { return sum + part_list->size(); });
236
237 return n_part * calc_transmit_size(box_geo, data_parts);
238}
239
240void pack_cells(CommBuf &buf, std::span<ParticleList *const> cells,
241 Utils::Vector3d const &shift, BoxGeometry const &box_geo,
242 unsigned data_parts) {
243 /* reallocate send buffer */
244 buf.resize(calc_transmit_size(cells, box_geo, data_parts));
245 buf.bonds().clear();
246
247 auto archiver = Utils::MemcpyOArchive{buf.make_span()};
248
249 /* Construct archive that pushes back to the bond buffer */
250 namespace io = boost::iostreams;
251 io::stream<io::back_insert_device<std::vector<char>>> os{
252 io::back_inserter(buf.bonds())};
253 boost::archive::binary_oarchive bond_archiver{os};
254
255 /* put in data */
256 for (auto part_list : cells) {
257 if (data_parts & GHOSTTRANS_PARTNUM) {
258 assert(part_list->size() <= std::numeric_limits<unsigned int>::max());
259 auto np = static_cast<unsigned int>(part_list->size());
260 archiver << np;
261 } else {
262 for (auto &p : *part_list) {
263 serialize_and_reduce(archiver, p, data_parts, ReductionPolicy::MOVE,
264 SerializationDirection::SAVE, box_geo, &shift);
265 if (data_parts & GHOSTTRANS_BONDS) {
266 bond_archiver << p.bonds();
267 }
268 }
269 }
270 }
271
272 assert(archiver.bytes_written() == buf.size());
273}
274
275void unpack_cells(CommBuf &buf, std::span<ParticleList *const> cells,
276 BoxGeometry const &box_geo, unsigned data_parts) {
277 /* put back data */
278 auto archiver = Utils::MemcpyIArchive{buf.make_span()};
279
280 if (data_parts & GHOSTTRANS_PARTNUM) {
281 for (auto part_list : cells) {
282 unsigned int np;
283 archiver >> np;
284 prepare_ghost_cell(part_list, np);
285 }
286 } else {
287 for (auto part_list : cells) {
288 for (auto &p : *part_list) {
289 serialize_and_reduce(archiver, p, data_parts, ReductionPolicy::MOVE,
290 SerializationDirection::LOAD, box_geo, nullptr);
291 }
292 }
293 if (data_parts & GHOSTTRANS_BONDS) {
294 namespace io = boost::iostreams;
295 io::stream<io::array_source> bond_stream(
296 io::array_source{buf.bonds().data(), buf.bonds().size()});
297 boost::archive::binary_iarchive bond_archiver(bond_stream);
298
299 for (auto part_list : cells) {
300 for (auto &p : *part_list) {
301 bond_archiver >> p.bonds();
302 }
303 }
304 }
305 }
306
307 assert(archiver.bytes_read() == buf.size());
308
309 buf.bonds().clear();
310}
311
312void add_forces(CommBuf &buf, std::span<ParticleList *const> cells,
313 unsigned data_parts) {
314 /* put back data */
315 auto archiver = Utils::MemcpyIArchive{buf.make_span()};
316 for (auto &part_list : cells) {
317 for (Particle &part : *part_list) {
318 if (data_parts & GHOSTTRANS_FORCE) {
319 Utils::Vector3d force;
320 archiver >> force;
321 part.force() += force;
322 }
323#ifdef ESPRESSO_ROTATION
324 if (data_parts & GHOSTTRANS_TORQUE) {
325 Utils::Vector3d torque;
326 archiver >> torque;
327 part.torque() += torque;
328 }
329#endif
330 }
331 }
332}
333
334#ifdef ESPRESSO_BOND_CONSTRAINT
335void add_rattle(CommBuf &buf, std::span<ParticleList *const> cells) {
336 /* put back data */
337 auto archiver = Utils::MemcpyIArchive{buf.make_span()};
338 for (auto &part_list : cells) {
339 for (Particle &part : *part_list) {
341 archiver >> pr;
342 part.rattle_params() += pr;
343 }
344 }
345}
346#endif
347
348#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
349void add_dip_fld(CommBuf &buf, std::span<ParticleList *const> cells) {
350 auto archiver = Utils::MemcpyIArchive{buf.make_span()};
351 for (auto &part_list : cells) {
352 for (Particle &part : *part_list) {
353 Utils::Vector3d dip_fld;
354 archiver >> dip_fld;
355 part.dip_fld() += dip_fld;
356 }
357 }
358}
359#endif
360
362 Utils::Vector3d const &shift, BoxGeometry const &box_geo,
363 unsigned data_parts) {
364 if (data_parts & GHOSTTRANS_PARTNUM) {
365 prepare_ghost_cell(&dst, src.size());
366 } else {
367 assert(src.size() == dst.size());
368 CommBuf buffer;
369 buffer.resize(calc_transmit_size(box_geo, data_parts));
370
371 for (std::size_t i = 0; i < src.size(); i++) {
372 auto ar_out = Utils::MemcpyOArchive{buffer.make_span()};
373 auto ar_in = Utils::MemcpyIArchive{buffer.make_span()};
374 auto &p1 = src.begin()[i];
375 auto &p2 = dst.begin()[i];
376 serialize_and_reduce(ar_out, p1, data_parts, ReductionPolicy::UPDATE,
377 SerializationDirection::SAVE, box_geo, &shift);
378 serialize_and_reduce(ar_in, p2, data_parts, ReductionPolicy::UPDATE,
379 SerializationDirection::LOAD, box_geo, nullptr);
380 if (data_parts & GHOSTTRANS_BONDS) {
381 p2.bonds() = p1.bonds();
382 }
383 }
384 }
385}
386
387} // namespace GhostComm
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.
std::size_t size() const
Returns the number of elements in the non-bond storage.
auto & bonds()
Returns a reference to the bond storage.
void resize(std::size_t new_size)
Resizes the underlying storage s.t.
Pseudo-archive to calculate the size of the serialization buffer.
iterator begin()
Definition Bag.hpp:82
std::size_t size() const
Number of elements in the container.
Definition Bag.hpp:90
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.
Ghost particles and particle exchange.
@ GHOSTTRANS_MOMENTUM
transfer ParticleMomentum
Definition ghosts.hpp:41
@ GHOSTTRANS_RATTLE
transfer ParticleRattle
Definition ghosts.hpp:46
@ GHOSTTRANS_QUAT
transfer orientation quaternion (pushed with position; runtime-conditional)
Definition ghosts.hpp:54
@ GHOSTTRANS_DIPFLD
transfer dipole field tracking data
Definition ghosts.hpp:60
@ GHOSTTRANS_PARTNUM
resize the receiver particle arrays to the size of the senders
Definition ghosts.hpp:49
@ GHOSTTRANS_POSITION
transfer ParticlePosition
Definition ghosts.hpp:39
@ GHOSTTRANS_PROPRTS
transfer ParticleProperties
Definition ghosts.hpp:37
@ GHOSTTRANS_FORCE
transfer ParticleForce
Definition ghosts.hpp:43
@ GHOSTTRANS_TORQUE
transfer torque (reduced with force; runtime-conditional)
Definition ghosts.hpp:56
@ GHOSTTRANS_BONDS
Definition ghosts.hpp:50
void unpack_cells(CommBuf &buf, std::span< ParticleList *const > cells, BoxGeometry const &box_geo, unsigned data_parts)
Unpack particle data from a communication buffer into cells.
void pack_cells(CommBuf &buf, std::span< ParticleList *const > cells, Utils::Vector3d const &shift, BoxGeometry const &box_geo, unsigned data_parts)
Pack particle data from cells into a communication buffer.
void local_cell_copy(ParticleList &src, ParticleList &dst, Utils::Vector3d const &shift, BoxGeometry const &box_geo, unsigned data_parts)
Copy particle data from src to dst applying a ghost shift.
std::size_t calc_transmit_size(BoxGeometry const &box_geo, unsigned data_parts)
Calculate the per-particle transmit size for the given data parts.
void add_forces(CommBuf &buf, std::span< ParticleList *const > cells, unsigned data_parts)
Add forces (and optionally torques) from a communication buffer to particles in cells.
SerializationDirection
Whether to save the state to or load the state from the archive.
void add_dip_fld(CommBuf &buf, std::span< ParticleList *const > cells)
Add dipole fields from a communication buffer to particles.
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.
ReductionPolicy
Type of reduction to carry out during serialization.
@ UPDATE
Reduction for cell-to-cell particle update.
@ MOVE
Reduction for domain-to-domain particle communication.
static void prepare_ghost_cell(ParticleList *cell, std::size_t size)
void add_rattle(CommBuf &buf, std::span< ParticleList *const > cells)
Add rattle corrections from a communication buffer to particles.
Reusable particle packing/unpacking for ghost communications.
Struct holding all information for one particle.
Definition Particle.hpp:436
constexpr auto const & dip_fld() const
Definition Particle.hpp:590
constexpr auto const & quat() const
Definition Particle.hpp:527
constexpr auto const & pos() const
Definition Particle.hpp:476
constexpr auto const & swimming() const
Definition Particle.hpp:653
constexpr auto const & rattle_correction() const
Definition Particle.hpp:661
constexpr auto const & rinertia() const
Definition Particle.hpp:593
constexpr auto const & mass() const
Definition Particle.hpp:500
constexpr auto const & dipm() const
Definition Particle.hpp:543
constexpr auto const & type() const
Definition Particle.hpp:459
constexpr auto const & omega() const
Definition Particle.hpp:531
constexpr auto const & ext_force() const
Definition Particle.hpp:646
constexpr auto const & propagation() const
Definition Particle.hpp:462
constexpr auto const & ext_torque() const
Definition Particle.hpp:534
constexpr auto const & rotation() const
Definition Particle.hpp:505
constexpr auto const & force() const
Definition Particle.hpp:480
constexpr auto const & vs_relative() const
Definition Particle.hpp:617
constexpr auto const & fixed() const
Definition Particle.hpp:629
constexpr auto const & gamma() const
Definition Particle.hpp:621
constexpr auto const & gamma_rot() const
Definition Particle.hpp:624
constexpr auto const & image_box() const
Definition Particle.hpp:489
constexpr auto const & mu_E() const
Definition Particle.hpp:602
constexpr auto const & id() const
Definition Particle.hpp:455
constexpr auto const & mol_id() const
Definition Particle.hpp:457
constexpr auto const & q() const
Definition Particle.hpp:597
constexpr auto const & pos_last_time_step() const
Definition Particle.hpp:657
constexpr auto const & v() const
Definition Particle.hpp:478
constexpr auto const & torque() const
Definition Particle.hpp:529