chombo-discharge
Loading...
Searching...
No Matches
CD_ParticleManagementImplem.H
Go to the documentation of this file.
1/* chombo-discharge
2 * Copyright © 2022 SINTEF Energy Research.
3 * Please refer to Copyright.txt and LICENSE in the chombo-discharge root directory.
4 */
5
12#ifndef CD_ParticleManagementImplem_H
13#define CD_ParticleManagementImplem_H
14
15// Std includes
16#include <utility>
17#include <type_traits>
18
19// Chombo includes
20#include <CH_Timer.H>
21
22// Our includes
23#include <CD_Random.H>
25#include <CD_NamespaceHeader.H>
26
27namespace ParticleManagement {
28
29 template <class P, Real& (P::*weight)(), const RealVect& (P::*position)() const>
30 static inline void
31 partitionAndSplitEqualWeightKD(KDNode<P>& a_node, const BinaryParticleReconcile<P> a_particleReconcile) noexcept
32 {
33 CH_assert(!(a_node.isInteriorNode()));
34 CH_assert(a_node.weight() > 2.0 - std::numeric_limits<Real>::min());
35
36 constexpr Real splitThresh = 2.0 - std::numeric_limits<Real>::min();
37
38 // Particles and node weight.
40
41 const Real W = a_node.weight();
42
43 // A. Figure out which coordinate direction we should partition and sort
44 // the particles.
45 RealVect loCorner = +std::numeric_limits<Real>::max() * RealVect::Unit;
46 RealVect hiCorner = -std::numeric_limits<Real>::max() * RealVect::Unit;
47
48 for (auto& p : particles) {
49 const RealVect& pos = (p.*position)();
50 for (int dir = 0; dir < SpaceDim; dir++) {
51 loCorner[dir] = std::min(pos[dir], loCorner[dir]);
52 hiCorner[dir] = std::max(pos[dir], hiCorner[dir]);
53 }
54 }
55
56 const int splitDir = (hiCorner - loCorner).maxDir(true);
57
58 auto sortCrit = [splitDir](const P& p1, const P& p2) -> bool {
59 return (p1.*position)()[splitDir] < (p2.*position)()[splitDir];
60 };
61
62 std::sort(particles.begin(), particles.end(), sortCrit);
63
64 // B. Determine the "median particle" and start computing the weight in the
65 // two halves.
66 size_t id = 0;
67 Real wl = 0.0;
68 Real wr = W - (particles[id].*weight)();
69
70 for (size_t i = 1; i < particles.size(); i++) {
71 const Real& w = (particles[id].*weight)();
72
73 if (wl + w < wr) {
74 id = i;
75 wl += w;
76 wr = W - wl - (particles[id].*weight)();
77 }
78 else {
79 break;
80 }
81 }
82
83 // C. Copy the two particle halves to each subnode.
84 P p = particles[id];
85
88
89 std::move(particles.begin(), particles.begin() + id, std::back_inserter(pl));
90 std::move(particles.begin() + id + 1, particles.end(), std::back_inserter(pr));
91
92 const Real& pw = (p.*weight)();
93 const Real dw = wr - wl;
94
95 CH_assert(wl + wr + pw == W);
96
97 // D. Assign the median particle; split the particle if we can.
98 if (pw >= splitThresh && pw >= std::abs(dw)) {
99 Real dwl = dw;
100 Real dwr = 0.0;
101 Real ddw = pw - dw;
102
103 const long long N = (long long)ddw;
104
105 if (N > 0LL) {
106
107 const long long Nr = N / 2;
108 const long long Nl = N - Nr;
109
110 dwl += (ddw / N) * Nl;
111 dwr += (ddw / N) * Nr;
112 }
113
114 if (dwl > 0.0 && dwr > 0.0) {
115 // Splitting particle.
116
117 P il(p);
118 P ir(p);
119
120 CH_assert(dwl >= 1.0);
121 CH_assert(dwr >= 1.0);
122
123 wl += dwl;
124 wr += dwr;
125
126 (il.*weight)() = dwl;
127 (ir.*weight)() = dwr;
128
129 // User can reconcile other particle properties.
131
132 pl.emplace_back(std::move(il));
133 pr.emplace_back(std::move(ir));
134 }
135 else if (dwl > 0.0 && dwr == 0.0) {
136 // Particle assigned to left node.
137
138 P il(p);
139
140 CH_assert(dwl >= 1.0);
141
142 wl += dwl;
143 (il.*weight)() = dwl;
144 pl.emplace_back(std::move(il));
145 }
146 else if (dwl == 0.0 && dwr > 0.0) {
147 // Particle assigned to right node.
148
149 P ir(p);
150
151 CH_assert(dwr >= 1.0);
152
153 wr += dwr;
154 (ir.*weight)() = dwr;
155 pr.emplace_back(std::move(ir));
156 }
157 else {
158 // Should not happen.
159
160 MayDay::Abort("ParticleManagement::partitionAndSplitEqualWeightKD - logic bust");
161 }
162 }
163 else {
164 if (wl <= wr) {
165 wl += pw;
166 pl.emplace_back(std::move(p));
167 }
168 else {
169 wr += pw;
170 pr.emplace_back(std::move(p));
171 }
172 }
173
174 // E. If this breaks, weight is not conserved or we broke the median particle splitting; the weight difference
175 // between the left/right node should be at most one physical particle.
176 CH_assert(wl + wr == W);
177 CH_assert(std::abs(wl - wr) <= 1.0);
178
179 // F. Instantiate the child nodes.
180 particles.resize(0);
181
183 a_node.getRight() = std::make_shared<KDNode<P>>(pr);
184
185 a_node.getLeft()->weight() = wl;
186 a_node.getRight()->weight() = wr;
187
188 // G. Debug code; make sure particle weights make sense
189#ifndef NDEBUG
190 Real WL = 0.0;
191 Real WR = 0.0;
192
193 // Note: Not auto& l/r : pl/pr because the particles were into the child node.
194 for (auto& l : a_node.getLeft()->getParticles()) {
195 CH_assert((l.*weight)() >= 1.0);
196
197 WL += (l.*weight)();
198 }
199
200 for (auto& r : a_node.getRight()->getParticles()) {
201 CH_assert((r.*weight)() >= 1.0);
202
203 WR += (r.*weight)();
204 }
205
206 CH_assert(WL == wl);
207 CH_assert(WR == wr);
208#endif
209 }
210
211 template <class P, Real& (P::*weight)(), const RealVect& (P::*position)() const>
213 recursivePartitionAndSplitEqualWeightKD(typename KDNode<P>::ParticleList& a_inputParticles,
214 const int a_maxLeaves,
216 {
217 CH_TIME("ParticleManagement::recursivePartitionAndSplitEqualWeightKD");
218
219 Real W = 0.0;
220
221 for (auto& p : a_inputParticles) {
222 W += (p.*weight)();
223 }
224
226
228 leaves[0]->weight() = W;
229
230 bool keepGoing = true;
231
232 while (keepGoing && leaves.size() < a_maxLeaves) {
233 keepGoing = false;
234
236
237 for (const auto& l : leaves) {
238 if (l->weight() > 2.0 - std::numeric_limits<Real>::min()) {
239 ParticleManagement::partitionAndSplitEqualWeightKD<P, weight, position>(*l, a_particleReconcile);
240
241 newLeaves.emplace_back(l->getLeft());
242 newLeaves.emplace_back(l->getRight());
243
244 keepGoing = true;
245 }
246 else {
247 newLeaves.emplace_back(l);
248 }
249
250 // Break out if we have sufficient leaf nodes.
251 if (newLeaves.size() >= a_maxLeaves) {
252 break;
253 }
254 }
255
257 }
258
259 return leaves;
260 }
261
262 template <typename P, typename T, typename>
263 inline void
264 removePhysicalParticles(List<P>& a_particles, const T a_numPhysPartToRemove) noexcept
265 {
266 CH_TIME("ParticleManagement::removePhysicalParticles");
267
268 constexpr T zero = (T)0;
269
270 // Obviously an error that the user should catch.
272 MayDay::Error("ParticleManagement::removePhysicalParticles - 'a_numPhysPartoToRemove < 0'");
273 }
274
275 if (a_particles.length() > 0) {
277
278 T numRemoved = zero;
279
280 // 1. Compute the minimum particle weight.
281 T minWeight = std::numeric_limits<T>::max();
282 for (lit.begin(); lit.ok(); ++lit) {
283 minWeight = std::min(minWeight, (T)lit().weight());
284 }
285
286 // 2. Trim particle weights down to minWeight.
287 for (lit.begin(); lit.ok(); ++lit) {
288 const T diff1 = (T)lit().weight() - minWeight;
290
291 CH_assert(diff1 >= zero);
292 CH_assert(diff2 >= zero);
293
294 const T r = std::max(0LL, std::min(diff1, diff2));
295
296 lit().weight() -= 1.0 * r;
297 numRemoved += r;
298 }
299
300 // 3. "Uniformly" subtract the particle weights.
302 const T numCompParticles = (T)a_particles.length();
305
306 // Uniformly remove weight from each particle.
307 if (uniformWeight > zero) {
308 for (lit.begin(); lit.ok(); ++lit) {
309 lit().weight() -= 1.0 * uniformWeight;
310
312 }
313 }
314
315 // May have to remove remainder from multiple particles because their weights might be less
316 // then the actual remainder.
317 if (uniformRemainder > zero) {
318 T W = 0;
319
320 for (lit.begin(); lit.ok(); ++lit) {
321
322 // Never remove so that weight is negative.
323 const T w = std::min((T)lit().weight(), uniformRemainder - W);
324
325 lit().weight() -= 1.0 * w;
326
327 W += w;
328 numRemoved += w;
329
330 if (W == uniformRemainder) {
331 break;
332 }
333 }
334 }
335 }
336
337 // Debug code.
339#ifdef NDEBUG
340 for (lit.begin(); lit.ok(); ++lit) {
341 CH_assert(lit().weight() >= 0.0);
342 }
343#endif
344 }
345 }
346
347 template <typename P>
348 inline void
349 deleteParticles(List<P>& a_particles, const Real a_weightThresh) noexcept
350 {
351 CH_TIME("ParticleManagement::deleteParticles");
352
353 for (ListIterator<P> lit(a_particles); lit.ok();) {
354 if (lit().weight() < a_weightThresh) {
355 a_particles.remove(lit);
356 }
357 else {
358 ++lit;
359 }
360 }
361 }
362
363 template <typename T, typename>
364 inline std::vector<T>
365 partitionParticleWeights(const T a_numPhysicalParticles, const T a_maxCompParticles) noexcept
366 {
368
369 constexpr T zero = (T)0;
370 constexpr T one = (T)1;
371
372 if (a_maxCompParticles > zero) {
375 }
376 else {
379
380 if (W > zero) {
381 ret.resize(a_maxCompParticles, W);
382
383 for (int i = 0; i < ret.size() && r > zero; i++) {
384 ret[i] += one;
385 r--;
386 }
387 }
388 else {
389 ret.resize(1, r);
390 }
391 }
392 }
393
394 return ret;
395 }
396
397 template <typename T, typename>
398 inline T
399 partitionParticles(const T a_numParticles)
400 {
401#ifdef CH_MPI
402 const T quotient = a_numParticles / numProc();
403 const T remainder = a_numParticles % numProc();
404
406
407 for (int i = 0; i < remainder; i++) {
409 }
410
411 return particlesPerRank[procID()];
412#else
413 return a_numParticles;
414#endif
415 }
416
417 template <typename P, typename T, typename>
418 inline void
419 drawRandomParticles(List<P>& a_particles, const T a_numParticles, const std::function<RealVect()>& a_distribution)
420 {
421
422 a_particles.clear();
423
424 const T numParticles = partitionParticles(a_numParticles);
425
426 for (T t = 0; t < numParticles; t++) {
427 P p;
428 p.position() = a_distribution();
429
430 a_particles.add(p);
431 }
432 }
433
434 template <typename P, typename T, typename>
435 inline void
436 drawSphereParticles(List<P>& a_particles,
437 const T a_numParticles,
438 const RealVect a_center,
439 const Real a_radius) noexcept
440 {
441 CH_TIME("ParticleManagement::drawSphereParticles");
442
443 a_particles.clear();
444
445 const T numParticles = partitionParticles(a_numParticles);
446
447 for (T t = 0; t < numParticles; t++) {
448 P p;
449
450 RealVect& x = p.position();
451
452 x = std::numeric_limits<Real>::max() * RealVect::Unit;
453
454 while (x.vectorLength() > a_radius) {
455 for (int d = 0; d < SpaceDim; d++) {
457 }
458 }
459
460 x += a_center;
461
462 a_particles.add(p);
463 }
464 }
465
466 template <typename P, typename T, typename>
467 inline void
468 drawBoxParticles(List<P>& a_particles,
469 const T a_numParticles,
470 const RealVect a_loCorner,
471 const RealVect a_hiCorner) noexcept
472 {
473 CH_TIME("ParticleManagement::drawBoxParticles");
474
476
477 auto ranBox = [&]() -> RealVect {
481 };
482
483 drawRandomParticles(a_particles, a_numParticles, ranBox);
484 }
485
486 template <typename P, typename T, typename>
487 inline void
488 drawGaussianParticles(List<P>& a_particles,
489 const T a_numParticles,
490 const RealVect a_center,
491 const Real a_radius) noexcept
492 {
493 CH_TIME("ParticleManagement::drawGaussianParticles");
494
496
497 auto ranGauss = [&]() -> RealVect {
499 };
500
501 drawRandomParticles(a_particles, a_numParticles, ranGauss);
502 }
503} // namespace ParticleManagement
504
505#include <CD_NamespaceFooter.H>
506
507#endif
Namespace containing various particle management utilities.
File containing some useful static methods related to random number generation.
std::vector< P > ParticleList
List of particles. This is aliased because the KD-tree construction may require both random access an...
Definition CD_KDNode.H:39
static Real get(T &a_distribution)
For getting a random number from a user-supplied distribution. T must be a distribution for which we ...
Definition CD_RandomImplem.H:221
static RealVect getDirection()
Get a random direction in space.
Definition CD_RandomImplem.H:182
static Real getUniformReal11()
Get a uniform real number on the interval [-1,1].
Definition CD_RandomImplem.H:162
static Real getUniformReal01()
Get a uniform real number on the interval [0,1].
Definition CD_RandomImplem.H:152
Base class for a tracer particle solver. This solver can advance particles in a pre-defined velocity ...
Definition CD_TracerParticleSolver.H:37
TracerParticleSolver()
Default constructor.
Definition CD_TracerParticleSolverImplem.H:25
virtual ParticleContainer< P > & getParticles()
Get all particles.
Definition CD_TracerParticleSolverImplem.H:662
RealVect position(const Location::Cell a_location, const VolIndex &a_vof, const EBISBox &a_ebisbox, const Real &a_dx)
Compute the position (ignoring the "origin) of a Vof.
Definition CD_LocationImplem.H:20
Namespace for various particle management tools.
Definition CD_ParticleManagement.H:31