ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
HaloExchange.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#pragma once
22
23/**
24 * @file
25 * Asynchronous, split-phase ghost-communication engine.
26 *
27 * Drive a @ref GhostComm::HaloPlan with non-blocking point-to-point MPI
28 * (plus same-rank copies), reusing the byte-identical serialization from
29 * @ref particle_packing.hpp. The engine is split into a
30 * @c halo_exchange_start phase (post all receives, pack and post all sends)
31 * and a @c halo_exchange_finish phase (same-rank copies, wait, unpack/reduce)
32 * so that callers can overlap other work with in-flight messages.
33 *
34 * Deadlock-freedom is guaranteed by posting every @c irecv before any
35 * @c isend and matching each message by @c (peer, tag); message sizes are
36 * known a-priori from the receiving cells' packed size (see
37 * @ref GhostComm::calc_transmit_size), except for the PARTNUM bootstrap.
38 */
39
40#include "BoxGeometry.hpp"
41#include "ghosts/HaloPlan.hpp"
43
44#include <boost/mpi/request.hpp>
45
46#include <memory>
47#include <vector>
48
49namespace GhostComm {
50
51/**
52 * @brief Persistent per-neighbor buffer pool for halo exchanges.
53 *
54 * Owns the heap-allocated send/recv buffers, MPI-request slots, and
55 * cell-pointer scratch arrays for one set of neighbor communications.
56 * Holding this object across multiple calls to @c halo_exchange allows
57 * the underlying @c std::vector storage to be reused via @c resize —
58 * after the first exchange (warm-up) the vectors retain capacity and
59 * subsequent calls incur no heap allocations on the hot path.
60 *
61 * Typical ownership: held as a member of the object that drives ghost
62 * communication (e.g. @c CellStructure::m_ghost_buffers) and passed to
63 * @c halo_exchange / @c halo_exchange_start as a mutable ref.
64 *
65 * Thread-safety: a single instance must not be used from concurrent threads.
66 * The sequential ghost-exchange loop (each exchange completes via
67 * @c halo_exchange_finish before the next starts) is therefore safe.
68 */
70 /** Per-neighbor packed send buffers (index-aligned with plan->neighbors). */
71 std::vector<CommBuf> send;
72 /** Per-neighbor recv buffers (index-aligned with plan->neighbors). */
73 std::vector<CommBuf> recv;
74 /** Outstanding non-blocking send/recv requests (cleared before each use). */
75 std::vector<boost::mpi::request> requests;
76 /**
77 * Scratch cell-pointer arrays for the packing routines. For the Reduce
78 * direction send/recv roles swap; we need the plain @c ParticleList* from
79 * the @c SendRegion list. These must outlive the pack/unpack calls.
80 */
81 std::vector<std::vector<ParticleList *>> send_cells;
82 std::vector<std::vector<ParticleList *>> recv_cells;
83 /**
84 * Scratch index map for the Overwrite (wait_any) path in
85 * @c halo_exchange_finish: maps active request slot -> original neighbor
86 * index. Sized to the current neighbor count in @c halo_exchange_start
87 * alongside the other per-neighbor vectors; capacity is retained across
88 * calls so that no heap allocation occurs on the hot position-push path
89 * after the first (warm-up) exchange.
90 */
91 std::vector<std::size_t> slot_to_neighbor;
92};
93
94/**
95 * @brief Opaque handle for one in-flight halo exchange.
96 *
97 * Records the per-call metadata (op, data parts, geometry, plan) and holds a
98 * pointer to the @ref ExchangeBuffers that back the in-flight messages. When
99 * a persistent @ref ExchangeBuffers is supplied by the caller (via the pool
100 * overload), @c owned is null and @c bufs points to the caller's pool (no
101 * ownership). When the no-pool convenience overload is used, @c owned holds
102 * the heap-allocated pool and @c bufs == @c owned.get(); the pool is freed
103 * automatically when the handle is destroyed — on every exit path, including
104 * the @c GHOSTTRANS_NONE early return in @c halo_exchange_finish.
105 *
106 * Created by @c halo_exchange_start and consumed exactly once by
107 * @c halo_exchange_finish.
108 */
111 unsigned data_parts = 0u;
112 BoxGeometry const *box = nullptr;
113 HaloPlan const *plan = nullptr;
114 /**
115 * Non-null only when the no-pool overload allocated the buffer pool.
116 * Destroyed (and the pool freed) when the @c GhostExchange goes out of
117 * scope, guaranteeing cleanup on every exit path.
118 */
119 std::unique_ptr<ExchangeBuffers> owned;
120 /** Non-owning pointer to the active buffer pool (caller's or @c owned). */
122};
123
124/**
125 * @brief Begin a halo exchange using a caller-owned buffer pool.
126 *
127 * The buffers in @p bufs are resized to the current neighbor count (retaining
128 * capacity) so that, after the first call (warm-up), no heap allocation
129 * occurs on the POSITION / FORCE hot path.
130 *
131 * @param plan Communication plan (peers, regions, local copies).
132 * @param box Box geometry for position folding.
133 * @param data_parts Bitmask of GHOSTTRANS_* flags to transfer.
134 * @param op Direction (Push/Reduce) and combine mode (Overwrite/Add).
135 * @param bufs Persistent buffer pool (must outlive the returned handle).
136 *
137 * @pre Each peer rank appears at most once in @p plan.neighbors. Multiple
138 * send/receive regions to the same peer **must** be folded into that
139 * peer's single @ref NeighborComm (as the plan builders do). This is
140 * what makes the @c (peer, data-part tag) message matching unambiguous:
141 * without this invariant two @c NeighborComm entries sharing the same
142 * peer and tag would cross-match, silently corrupting data or deadlocking.
143 * The plan builders (e.g. RegularDecomposition) always produce unique
144 * peers; the invariant is asserted at runtime when
145 * @c ESPRESSO_ADDITIONAL_CHECKS is defined.
146 */
148 unsigned data_parts, ExchangeOp op,
149 ExchangeBuffers &bufs);
150
151/**
152 * @brief Convenience overload that allocates a temporary @ref ExchangeBuffers.
153 *
154 * Use this form when buffer reuse across calls is not needed (e.g. unit tests,
155 * cold resort paths). Equivalent to the original single-call allocation.
156 */
158 unsigned data_parts, ExchangeOp op);
159
160/**
161 * @brief Complete a halo exchange: run same-rank copies (overlapping the
162 * in-flight messages), wait for all requests, then unpack or reduce.
163 */
165
166/**
167 * @brief Blocking wrapper using a caller-owned buffer pool (no per-call alloc
168 * after warm-up).
169 */
170void halo_exchange(HaloPlan const &plan, BoxGeometry const &box,
171 unsigned data_parts, ExchangeOp op, ExchangeBuffers &bufs);
172
173/**
174 * @brief Blocking convenience wrapper: start + finish (allocates a temporary
175 * @ref ExchangeBuffers; use the pool overload on hot paths).
176 */
177void halo_exchange(HaloPlan const &plan, BoxGeometry const &box,
178 unsigned data_parts, ExchangeOp op);
179
180} // namespace GhostComm
void halo_exchange_finish(GhostExchange &st)
Complete a halo exchange: run same-rank copies (overlapping the in-flight messages),...
void halo_exchange(HaloPlan const &plan, BoxGeometry const &box, unsigned data_parts, ExchangeOp op, ExchangeBuffers &bufs)
Blocking wrapper using a caller-owned buffer pool (no per-call alloc after warm-up).
GhostExchange halo_exchange_start(HaloPlan const &plan, BoxGeometry const &box, unsigned data_parts, ExchangeOp op, ExchangeBuffers &bufs)
Begin a halo exchange using a caller-owned buffer pool.
Reusable particle packing/unpacking for ghost communications.
Persistent per-neighbor buffer pool for halo exchanges.
std::vector< CommBuf > send
Per-neighbor packed send buffers (index-aligned with plan->neighbors).
std::vector< std::size_t > slot_to_neighbor
Scratch index map for the Overwrite (wait_any) path in halo_exchange_finish: maps active request slot...
std::vector< CommBuf > recv
Per-neighbor recv buffers (index-aligned with plan->neighbors).
std::vector< std::vector< ParticleList * > > send_cells
Scratch cell-pointer arrays for the packing routines.
std::vector< std::vector< ParticleList * > > recv_cells
std::vector< boost::mpi::request > requests
Outstanding non-blocking send/recv requests (cleared before each use).
Opaque handle for one in-flight halo exchange.
BoxGeometry const * box
ExchangeBuffers * bufs
Non-owning pointer to the active buffer pool (caller's or owned).
std::unique_ptr< ExchangeBuffers > owned
Non-null only when the no-pool overload allocated the buffer pool.