chombo-discharge
Loading...
Searching...
No Matches
CD_ParticleSoAImplem.H
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2021-2026 SINTEF Energy Research
3 *
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
13#ifndef CD_PARTICLESOAIMPLEM_H
14#define CD_PARTICLESOAIMPLEM_H
15
16// Std includes
17#include <cmath>
18#include <cstring>
19
20// Chombo includes
21#include <MayDay.H>
22
23// Our includes
24#include <CD_ParticleSoA.H>
25#include <CD_NamespaceHeader.H>
26
27template <typename P, typename Traits>
28inline void
29ParticleSoA<P, Traits>::reallocate(const std::size_t a_capacity)
30{
31 std::array<std::size_t, s_numColumns> off{};
32 const std::size_t total = this->computeOffsets(a_capacity, off, std::make_index_sequence<s_numColumns>{});
33
34 const std::size_t bytes = alignUp(total);
35
36 void* const newBuf = std::aligned_alloc(s_align, bytes);
37
38 if (newBuf == nullptr) {
39 MayDay::Abort("ParticleSoA::reallocate - std::aligned_alloc returned null (out of memory)");
40 }
41
42 if (m_size > 0) {
43 this->moveColumns(newBuf, off, std::make_index_sequence<s_numColumns>{});
44 }
45
48
49 std::free(m_buffer);
50
51 m_buffer = newBuf;
52 m_capacity = a_capacity;
53 m_bytes = bytes;
54
55 this->setColPtrs(off, std::make_index_sequence<s_numColumns>{});
56}
57
58template <typename P, typename Traits>
59inline void
60ParticleSoA<P, Traits>::permute(const std::vector<std::size_t>& a_dest)
61{
62 std::array<std::size_t, s_numColumns> off{};
63 const std::size_t total = this->computeOffsets(m_capacity, off, std::make_index_sequence<s_numColumns>{});
64
65 const std::size_t bytes = alignUp(total);
66
67 void* const newBuf = std::aligned_alloc(s_align, bytes);
68
69 if (newBuf == nullptr) {
70 MayDay::Abort("ParticleSoA::permute - std::aligned_alloc returned null (out of memory)");
71 }
72
73 this->scatterColumns(newBuf, off, a_dest, std::make_index_sequence<s_numColumns>{});
74
77
78 std::free(m_buffer);
79
80 m_buffer = newBuf;
81 m_bytes = bytes;
82
83 this->setColPtrs(off, std::make_index_sequence<s_numColumns>{});
84}
85
86template <typename P, typename Traits>
87inline void
88ParticleSoA<P, Traits>::reserve(const std::size_t a_capacity)
89{
90 if (a_capacity <= m_capacity) {
91 return;
92 }
93
94 this->reallocate(a_capacity);
95}
96
97template <typename P, typename Traits>
98inline void
99ParticleSoA<P, Traits>::resize(const std::size_t a_count)
100{
101 this->reserve(a_count);
102
103 m_size = a_count;
104 m_sorted = false;
105}
106
107template <typename P, typename Traits>
110{
112 dst.reserve(m_capacity); // identical arena layout: reallocate() sets the capacity exactly
113
114 if (m_size > 0) {
115 std::memcpy(dst.m_buffer, m_buffer, this->byteSpan()); // one contiguous copy of the live region
116 }
117
118 dst.m_size = m_size;
119 dst.m_sorted = m_sorted;
120 dst.m_cellStart = m_cellStart;
121 dst.m_sortBox = m_sortBox;
122 dst.m_sortDx = m_sortDx;
123 dst.m_sortProbLo = m_sortProbLo;
124
125 return dst;
126}
127
128template <typename P, typename Traits>
129inline void
131{
132 if (this == &a_dst) {
133 return; // copying onto self is a no-op
134 }
135
136 a_dst.clear(); // drop a_dst's particles (keeps its arena) so the grow below moves nothing
137 a_dst.reserve(m_size); // reuse a_dst's allocation if it is already large enough; grow only if not
138
139 if (m_size > 0) {
140 this->copyLiveColumnsTo(a_dst, std::make_index_sequence<s_numColumns>{});
141 }
142
143 a_dst.m_size = m_size;
144 a_dst.m_sorted = m_sorted;
145 a_dst.m_cellStart = m_cellStart;
146 a_dst.m_sortBox = m_sortBox;
147 a_dst.m_sortDx = m_sortDx;
148 a_dst.m_sortProbLo = m_sortProbLo;
149}
150
151template <typename P, typename Traits>
152inline void
154{
155 const std::size_t n = a_other.m_size;
156
157 if (n == 0) {
158 return;
159 }
160
161 const std::size_t oldSize = m_size;
162
163 this->growTo(oldSize + n); // may reallocate this (and a_other, when a_other == this); pointers refreshed below
164 this->appendColumnsFrom(a_other, oldSize, std::make_index_sequence<s_numColumns>{});
165
166 m_size = oldSize + n;
167 m_sorted = false;
168}
169
170template <typename P, typename Traits>
171inline void
173{
174 CH_assert(a_index < a_src.m_size);
175
176 const std::size_t at = m_size;
177
178 this->growTo(m_size + 1); // may reallocate this (and a_src, when a_src == this); pointers refreshed below
179 this->copyParticleColumns(a_src, a_index, at, std::make_index_sequence<s_numColumns>{});
180
181 m_size = at + 1;
182 m_sorted = false;
183}
184
185template <typename P, typename Traits>
186inline void
188{
189 if (m_capacity <= m_size) {
190 return; // already compact
191 }
192
193 if (m_size == 0) {
195
196 std::free(m_buffer);
197
198 m_buffer = nullptr;
199 m_capacity = 0;
200 m_bytes = 0;
201
202 m_colPtrs.fill(nullptr);
203
204 return;
205 }
206 this->reallocate(m_size);
207}
208
209template <typename P, typename Traits>
210inline void
211ParticleSoA<P, Traits>::append(const RealVect& a_position, const double a_weight, const P& a_payload)
212{
213 this->growIfFull();
214
215 for (int dir = 0; dir < SpaceDim; dir++) {
216 this->positionColumn(dir)[m_size] = a_position[dir]; // write the reserved slot directly (m_size not yet bumped)
217 }
218
219 this->template columnByIndex<s_weightCol>()[m_size] = a_weight;
220 this->template columnByIndex<s_idCol>()[m_size] = s_invalidID;
221 this->template columnByIndex<s_rankCol>()[m_size] = static_cast<RankID>(-1);
222 this->template columnByIndex<s_ghostCol>()[m_size] = GhostType::Valid;
223 this->writePayload(a_payload, std::make_index_sequence<s_numPayloadColumns>{});
224
225 m_size++;
226
227 m_sorted = false;
228}
229
230template <typename P, typename Traits>
231inline void
232ParticleSoA<P, Traits>::linearizeParticle(void* a_buffer, const std::size_t a_index) const noexcept
233{
234 CH_assert(a_index < m_size);
235
236 auto* p = static_cast<unsigned char*>(a_buffer);
237
238 this->linearizeImpl(p, a_index, std::make_index_sequence<s_numColumns>{});
239}
240
241template <typename P, typename Traits>
242inline void
244{
245 this->growIfFull();
246
247 const auto* p = static_cast<const unsigned char*>(a_buffer);
248
249 this->delinearizeImpl(p, std::make_index_sequence<s_numColumns>{});
250
251 m_size++;
252
253 m_sorted = false;
254}
255
256template <typename P, typename Traits>
257inline void
258ParticleSoA<P, Traits>::h5LinearizeParticle(void* a_buffer, const std::size_t a_index) const noexcept
259{
260 CH_assert(a_index < m_size);
261
262 auto* p = static_cast<unsigned char*>(a_buffer);
263
264 for (int dir = 0; dir < SpaceDim; dir++) {
265 detail::pushBytes(p, this->positionColumn(dir)[a_index]);
266 }
267
268 detail::pushBytes(p, this->template columnByIndex<s_weightCol>()[a_index]);
269
270 this->h5LinearizePayload(p, a_index, H5PayloadColumns{});
271}
272
273template <typename P, typename Traits>
274inline void
276{
277 this->growIfFull();
278
279 const auto* p = static_cast<const unsigned char*>(a_buffer);
280
281 for (int dir = 0; dir < SpaceDim; dir++) {
282 detail::pullBytes(p, this->positionColumn(dir)[m_size]);
283 }
284
285 detail::pullBytes(p, this->template columnByIndex<s_weightCol>()[m_size]);
286
287 this->template columnByIndex<s_idCol>()[m_size] = s_invalidID;
288 this->template columnByIndex<s_rankCol>()[m_size] = static_cast<RankID>(-1);
289 this->template columnByIndex<s_ghostCol>()[m_size] = GhostType::Valid;
290
291 // Non-checkpointed payload columns default-construct; then overwrite the checkpointed subset.
292 const P def{};
293
294 this->writePayload(def, std::make_index_sequence<s_numPayloadColumns>{});
295 this->h5DelinearizePayload(p, H5PayloadColumns{});
296
297 m_size++;
298 m_sorted = false;
299}
300
301template <typename P, typename Traits>
302inline void
303ParticleSoA<P, Traits>::sortByCell(const Box& a_box, const RealVect& a_dx, const RealVect& a_probLo)
304{
305 // No CH_TIME here: this leaf container is the per-particle hot path; timing of bulk
306 // operations like this one is done by the container/mesh drivers above.
307
308 // Already sorted against exactly this cell domain -- the sort below would reproduce the CSR mapping
309 // the container already holds. Callers establish this precondition defensively at several call sites
310 // (see sortByCell's documentation), so this is the common path, not a rare one.
311 if (this->isSortedAgainst(a_box, a_dx, a_probLo)) {
312 CH_assert(this->checkCellSort(a_box, a_dx, a_probLo));
313
314 return;
315 }
316
317 const std::size_t nCells = a_box.numPts();
318
319 m_sortBox = a_box;
320 m_sortDx = a_dx;
321 m_sortProbLo = a_probLo;
322
323 m_cellStart.assign(nCells + 1, 0);
324 if (m_size == 0) {
325 m_sorted = true;
326 return;
327 }
328
329 // Pass 1: compute each particle's Fortran cell key and tally per-cell counts.
330 std::vector<std::size_t> key(m_size);
331 for (std::size_t i = 0; i < m_size; i++) {
332 const std::size_t lin = this->cellKey(i, a_box, a_dx, a_probLo);
333
334 key[i] = lin;
335 m_cellStart[lin + 1]++;
336 }
337
338 // Prefix-sum the counts into CSR start offsets (m_cellStart[c] = start of cell c).
339 for (std::size_t c = 1; c <= nCells; c++) {
340 m_cellStart[c] += m_cellStart[c - 1];
341 }
342
343 // Pass 2: stable destination slot per particle, then physically reorder all columns.
344 std::vector<std::size_t> cursor(m_cellStart.begin(), m_cellStart.begin() + nCells);
345 std::vector<std::size_t> dest(m_size);
346
347 for (std::size_t i = 0; i < m_size; i++) {
348 dest[i] = cursor[key[i]]++;
349 }
350
351 this->permute(dest);
352
353 m_sorted = true;
354}
355
356#include <CD_NamespaceFooter.H>
357
358#endif
Declaration of ParticleSoA, an arena-backed Struct-of-Arrays particle container.
std::int32_t RankID
Owning-rank identifier type (container-owned metadata column; fixed-width for I/O).
Definition CD_ParticleSoA.H:166
@ Valid
Owned, valid particle of this patch (not a ghost).
Arena-backed Struct-of-Arrays particle container for a single grid patch.
Definition CD_ParticleSoA.H:655
void permute(const std::vector< std::size_t > &a_dest)
Reorder every column in place by a_dest (a_dest[i] = new slot of old particle i).
Definition CD_ParticleSoAImplem.H:60
void sortByCell(const Box &a_box, const RealVect &a_dx, const RealVect &a_probLo)
Counting-sort the columns into Fortran cell order and build CSR cell offsets.
Definition CD_ParticleSoAImplem.H:303
void append(const RealVect &a_position, const double a_weight)
Append one particle with a default-constructed payload.
Definition CD_ParticleSoA.H:955
void shrinkToFit()
Reclaim unused capacity by reallocating the arena down to the current size.
Definition CD_ParticleSoAImplem.H:187
bool m_sorted
Whether m_cellStart is valid.
Definition CD_ParticleSoA.H:1602
void delinearizeAndAppend(const void *a_buffer)
Append a particle delinearized from a byte buffer (all columns, for MPI receive).
Definition CD_ParticleSoAImplem.H:243
void linearizeParticle(void *a_buffer, const std::size_t a_index) const noexcept
Linearize particle i into a byte buffer (all columns, for MPI send).
Definition CD_ParticleSoAImplem.H:232
void deepCopyTo(ParticleSoA &a_dst) const
Deep copy into an existing destination, reusing its arena when possible.
Definition CD_ParticleSoAImplem.H:130
std::size_t m_size
Live particle count.
Definition CD_ParticleSoA.H:1597
typename detail::H5ColumnSubset< Traits, std::make_index_sequence< s_numPayloadColumns > >::Type H5PayloadColumns
The HDF5 payload column subset (indices into the payload columns).
Definition CD_ParticleSoA.H:759
ParticleSoA deepCopy() const
Explicit deep copy: a new container with a private arena duplicating every particle.
Definition CD_ParticleSoAImplem.H:109
void h5DelinearizeAndAppend(const void *a_buffer)
Append a particle whose HDF5-checkpointed columns come from a buffer.
Definition CD_ParticleSoAImplem.H:275
void h5LinearizeParticle(void *a_buffer, const std::size_t a_index) const noexcept
Linearize the HDF5-checkpointed columns of particle i (no id/rank).
Definition CD_ParticleSoAImplem.H:258
void * m_buffer
The single arena allocation.
Definition CD_ParticleSoA.H:1577
void resize(const std::size_t a_count)
Set the particle count (grows capacity if needed; new slots are uninitialized).
Definition CD_ParticleSoAImplem.H:99
std::vector< std::size_t > m_cellStart
CSR cell offsets (size numCells+1 when sorted).
Definition CD_ParticleSoA.H:1612
RealVect m_sortDx
Grid spacing the CSR mapping was built against (meaningful only when m_sorted).
Definition CD_ParticleSoA.H:1622
RealVect m_sortProbLo
Lower domain corner the CSR mapping was built against (meaningful only when m_sorted).
Definition CD_ParticleSoA.H:1627
Box m_sortBox
Patch box the CSR mapping in m_cellStart was built against (meaningful only when m_sorted).
Definition CD_ParticleSoA.H:1617
void clear() noexcept
Drop all particles (keeps the arena; invalidates the cell sort).
Definition CD_ParticleSoA.H:911
void appendParticle(const ParticleSoA &a_src, const std::size_t a_index)
Append a single particle (all columns, incl. id/rank) copied from another container.
Definition CD_ParticleSoAImplem.H:172
void reallocate(const std::size_t a_capacity)
Allocate a new arena and reallocate/move the columns into it (growth path).
Definition CD_ParticleSoAImplem.H:29
void reserve(const std::size_t a_capacity)
Ensure capacity for at least a_capacity particles (reallocates + moves on growth).
Definition CD_ParticleSoAImplem.H:88
void removeBytes(const Kind a_kind, const std::size_t a_bytes) noexcept
Record a release.
Definition CD_ParticleMemory.H:140
void addBytes(const Kind a_kind, const std::size_t a_bytes) noexcept
Record an allocation.
Definition CD_ParticleMemory.H:124
@ Container
ParticleSoA arenas: capacity a rank is holding.