chombo-discharge
CD_ParticleOpsImplem.H
Go to the documentation of this file.
1 /* chombo-discharge
2  * Copyright © 2021 SINTEF Energy Research.
3  * Please refer to Copyright.txt and LICENSE in the chombo-discharge root directory.
4  */
5 
12 #ifndef CD_ParticleOpsImplem_H
13 #define CD_ParticleOpsImplem_H
14 
15 // Chombo includes
16 #include <CH_Timer.H>
17 #include <PolyGeom.H>
18 
19 // Our includes
20 #include <CD_ParticleOps.H>
21 #include <CD_PolyUtils.H>
22 #include <CD_Random.H>
23 #include <CD_NamespaceHeader.H>
24 
25 inline IntVect
26 ParticleOps::getParticleCellIndex(const RealVect& a_particlePosition,
27  const RealVect& a_probLo,
28  const Real& a_dx) noexcept
29 {
30  return IntVect(D_DECL(std::floor((a_particlePosition[0] - a_probLo[0]) / a_dx),
31  std::floor((a_particlePosition[1] - a_probLo[1]) / a_dx),
32  std::floor((a_particlePosition[2] - a_probLo[2]) / a_dx)));
33 }
34 
35 inline IntVect
36 ParticleOps::getParticleCellIndex(const RealVect& a_particlePosition,
37  const RealVect& a_probLo,
38  const RealVect& a_dx) noexcept
39 {
40  return IntVect(D_DECL(std::floor((a_particlePosition[0] - a_probLo[0]) / a_dx[0]),
41  std::floor((a_particlePosition[1] - a_probLo[1]) / a_dx[1]),
42  std::floor((a_particlePosition[2] - a_probLo[2]) / a_dx[2])));
43 }
44 
45 template <typename P, const Real& (P::*weight)() const>
46 inline void
48 {
49  CH_TIME("ParticleOps::getPhysicalParticlesPerCell");
50 
51  const RealVect probLo = a_src.getProbLo();
52 
53  for (int lvl = 0; lvl <= a_src.getFinestLevel(); lvl++) {
54  const DisjointBoxLayout& dbl = a_src.getGrids()[lvl];
55  const DataIterator& dit = dbl.dataIterator();
56  const RealVect dx = a_src.getDx()[lvl];
57 
58  const int nbox = dit.size();
59 
60 #pragma omp parallel for schedule(runtime)
61  for (int mybox = 0; mybox < nbox; mybox++) {
62  const DataIndex& din = dit[mybox];
63 
64  const List<P>& particles = a_src[lvl][din].listItems();
65 
66  FArrayBox& ppc = (*a_ppc[lvl])[din].getFArrayBox();
67  ppc.setVal(0.0);
68 
69  for (ListIterator<P> lit(particles); lit.ok(); ++lit) {
70  const P& p = lit();
71  const RealVect x = p.position();
72  const Real w = (p.*weight)();
73  const IntVect iv = ParticleOps::getParticleGridCell(x, probLo, dx);
74 
75  ppc(iv, 0) += w;
76  }
77  }
78  }
79 }
80 
81 template <typename P>
82 inline void
84 {
85  CH_TIME("ParticleOps::getComputationalParticlesPerCell");
86 
87  const RealVect probLo = a_src.getProbLo();
88 
89  for (int lvl = 0; lvl <= a_src.getFinestLevel(); lvl++) {
90  const DisjointBoxLayout& dbl = a_src.getGrids()[lvl];
91  const DataIterator& dit = dbl.dataIterator();
92  const RealVect dx = a_src.getDx()[lvl];
93 
94  const int nbox = dit.size();
95 
96 #pragma omp parallel for schedule(runtime)
97  for (int mybox = 0; mybox < nbox; mybox++) {
98  const DataIndex& din = dit[mybox];
99 
100  const List<P>& particles = a_src[lvl][din].listItems();
101 
102  FArrayBox& ppc = (*a_ppc[lvl])[din].getFArrayBox();
103  ppc.setVal(0.0);
104 
105  for (ListIterator<P> lit(particles); lit.ok(); ++lit) {
106  const P& p = lit();
107  const RealVect x = p.position();
108  const IntVect iv = ParticleOps::getParticleGridCell(x, probLo, dx);
109 
110  ppc(iv, 0) += 1.0;
111  }
112  }
113  }
114 }
115 
116 inline IntVect
117 ParticleOps::getParticleGridCell(const RealVect& a_particlePosition,
118  const RealVect& a_probLo,
119  const RealVect& a_dx) noexcept
120 {
121  return IntVect(D_DECL(std::floor((a_particlePosition[0] - a_probLo[0]) / a_dx[0]),
122  std::floor((a_particlePosition[1] - a_probLo[1]) / a_dx[1]),
123  std::floor((a_particlePosition[2] - a_probLo[2]) / a_dx[2])));
124 }
125 
126 inline bool
127 ParticleOps::domainIntersection(const RealVect& a_oldPos,
128  const RealVect& a_newPos,
129  const RealVect& a_probLo,
130  const RealVect& a_probHi,
131  Real& a_s)
132 {
133 
134  // TLDR: This code does a boundary intersection test and returns where on the interval [oldPos, newPos] the intersection
135  // happened. We do this by checking if the particle moves towards a particular domain side and ends up outside of it.
136 
138 
139  bool crossedDomainBoundary = false;
140 
141  const RealVect path = a_newPos - a_oldPos;
142 
143  for (int dir = 0; dir < SpaceDim; dir++) {
144  for (SideIterator sit; sit.ok(); ++sit) {
145  const Side::LoHiSide side = sit();
146  const RealVect wallPoint = (side == Side::Lo) ? a_probLo : a_probHi; // A point on the domain side
147  const RealVect n0 = sign(side) *
148  RealVect(
149  BASISV(dir)); // Normal vector pointing OUT of the domain on side sit and direction dir.
150  const Real normPath = PolyGeom::dot(n0, path); // Component of the path that is normal to the domain edge/face.
151 
152  // If normPath > 0 then the particle trajectory points towards the domain edge/face and we can have an intersection.
153  if (normPath > 0.0) {
154 
155  // s determines the intersection point between the particle path and the plane corresponding to the domain edge/face. Note that
156  // we consider the edge/face to be an infinite plane and we just compute the intersection point between each edge/face and select the
157  // closest intersection point.
158  const Real s = PolyGeom::dot(wallPoint - a_oldPos, n0) / normPath;
159  if (s >= 0.0 && s <= 1.0) {
160  crossedDomainBoundary = true;
161  if (s < a_s) {
162  a_s = s;
163  }
164  }
165  }
166  }
167  }
168 
169  return crossedDomainBoundary;
170 }
171 
172 inline bool
173 ParticleOps::ebIntersectionBisect(const RefCountedPtr<BaseIF>& a_impFunc,
174  const RealVect& a_oldPos,
175  const RealVect& a_newPos,
176  const Real& a_bisectStep,
177  Real& a_s)
178 {
179 
180  // TLDR: We compute the intersection point using a bisection algorithm. We divide the full path into intervals and check if an interval
181  // has a root. If it does, we compute it using Brent's algorithm.
182 
184 
185  bool crossedEB = false;
186 
187  const Real pathLen = (a_newPos - a_oldPos).vectorLength(); // Total path len
188  const int nsteps = ceil(pathLen / a_bisectStep); // Number of bisection intervals
189  const RealVect dxStep = (a_newPos - a_oldPos) / nsteps; // Physical length of each bisection interval
190 
191  // Check each interval
192  RealVect curPos = a_oldPos;
193  for (int istep = 0; istep < nsteps; istep++) {
194  const Real fa = a_impFunc->value(curPos); // Value of the implicit function at the start of the bisection interval
195  const Real fb = a_impFunc->value(curPos +
196  dxStep); // Value of the implicit function at the end of the bisection interval
197 
198  if (fa * fb <= 0.0) {
199 
200  // If this triggered we happen to know that f(pos+dxStep) > 0.0 and f(pos) < 0.0 and so we must have a root on the interval. We now compute the precise location
201  // where the particle crossed the EB. For that we use a Brent root finder on the interval [pos, pos+dxStep]. This is a 1D problem.
202  const RealVect intersectionPos = PolyUtils::brentRootFinder(a_impFunc, curPos, curPos + dxStep);
203  a_s = (intersectionPos - a_oldPos).vectorLength() / pathLen;
204  crossedEB = true;
205 
206  break;
207  }
208  else { // Move to next interval
209  curPos += dxStep;
210  }
211  }
212 
213  return crossedEB;
214 }
215 
216 inline bool
217 ParticleOps::ebIntersectionRaycast(const RefCountedPtr<BaseIF>& a_impFunc,
218  const RealVect& a_oldPos,
219  const RealVect& a_newPos,
220  const Real& a_tolerance,
221  Real& a_s)
222 {
223 
225 
226  bool ret = false;
227 
228  // Absolute distance to EB.
229  auto dist = [&](const RealVect& x) -> Real {
230  return std::abs(a_impFunc->value(x));
231  };
232 
233  const Real D = (a_newPos - a_oldPos).vectorLength(); // Total particle path length
234  const Real D0 = dist(a_oldPos); // Distance to EB from starting position
235 
236  // If the distance to the EB from the starting position is smaller than the total path length, we need to check for intersections.
237  if (D > D0) {
238 
239  const RealVect t = (a_newPos - a_oldPos) / D; // Particle trajectory.
240 
241  // Move a_oldPos along +t. If we end up too close to the boundary the particle has intersected the BC. Note that this does NOT check for whether or not
242  // the particle moves tangential to the EB surface. The length of each step is the distance to the EB, so if the particle is close to the EB but moves
243  // tangentially to it, this routine will be EXTREMELY slow.
244  RealVect xa = a_oldPos;
245  Real r = D;
246  Real d = dist(xa);
247 
248  while (d < r) {
249 
250  if (d < a_tolerance) { // We collided.
251  a_s = (xa - a_oldPos).vectorLength() / D;
252  ret = true;
253  break;
254  }
255  else { // We did not collide.
256  xa += t * d;
257  r -= d;
258  d = dist(xa);
259  }
260  }
261  }
262 
263  return ret;
264 }
265 
266 template <typename P>
267 inline void
269 {
270  CH_TIME("ParticleOps::copy(ParticleContainer<P> x2)");
271 
272  CH_assert(a_dst.getRealm() == a_src.getRealm());
273 
274  for (int lvl = 0; lvl <= a_dst.getFinestLevel(); lvl++) {
275  const DisjointBoxLayout& dbl = a_dst.getGrids()[lvl];
276  const DataIterator& dit = dbl.dataIterator();
277 
278  const int nbox = dit.size();
279 
280 #pragma omp parallel for schedule(runtime)
281  for (int mybox = 0; mybox < nbox; mybox++) {
282  const DataIndex& din = dit[mybox];
283 
284  a_dst[lvl][din].listItems() = a_src[lvl][din].listItems();
285  }
286  }
287 }
288 
289 template <typename P>
290 inline void
292 {
293  CH_TIME("ParticleOps::copyDestructive(ParticleContainer<P> x2)");
294 
295  CH_assert(a_dst.getRealm() == a_src.getRealm());
296 
297  for (int lvl = 0; lvl <= a_dst.getFinestLevel(); lvl++) {
298  const DisjointBoxLayout& dbl = a_dst.getGrids()[lvl];
299  const DataIterator& dit = dbl.dataIterator();
300 
301  const int nbox = dit.size();
302 
303 #pragma omp parallel for schedule(runtime)
304  for (int mybox = 0; mybox < nbox; mybox++) {
305  const DataIndex& din = dit[mybox];
306 
307  a_dst[lvl][din].listItems() = a_src[lvl][din].listItems();
308 
309  a_src[lvl][din].listItems().clear();
310  }
311  }
312 }
313 
314 template <typename P, const Real& (P::*scalarQuantity)() const>
315 inline Real
316 ParticleOps::sum(const ParticleContainer<P>& a_particles) noexcept
317 {
318  CH_TIME("ParticleOps::sum(ParticleContainer<P>)");
319 
320  Real particleSum = 0.0;
321 
322  for (int lvl = 0; lvl <= a_particles.getFinestLevel(); lvl++) {
323  const DisjointBoxLayout& dbl = a_particles.getGrids()[lvl];
324  const DataIterator& dit = dbl.dataIterator();
325 
326  const int nbox = dit.size();
327 
328 #pragma omp parallel for schedule(runtime) reduction(+ : particleSum)
329  for (int mybox = 0; mybox < nbox; mybox++) {
330  const DataIndex& din = dit[mybox];
331 
332  const List<P>& particles = a_particles[lvl][din].listItems();
333 
334  for (ListIterator<P> lit(particles); lit.ok(); ++lit) {
335  particleSum += (lit().*scalarQuantity)();
336  }
337  }
338  }
339 
340  return ParallelOps::sum(particleSum);
341 }
342 
343 template <typename P, Real (P::*scalarQuantity)()>
344 inline Real
345 ParticleOps::sum(const ParticleContainer<P>& a_particles) noexcept
346 {
347  CH_TIME("ParticleOps::sum(ParticleContainer<P>)");
348 
349  Real particleSum = 0.0;
350 
351  for (int lvl = 0; lvl <= a_particles.getFinestLevel(); lvl++) {
352  const DisjointBoxLayout& dbl = a_particles.getGrids()[lvl];
353  const DataIterator& dit = dbl.dataIterator();
354 
355  const int nbox = dit.size();
356 
357 #pragma omp parallel for schedule(runtime) reduction(+ : particleSum)
358  for (int mybox = 0; mybox < nbox; mybox++) {
359  const DataIndex& din = dit[mybox];
360 
361  const List<P>& particles = a_particles[lvl][din].listItems();
362 
363  for (ListIterator<P> lit(particles); lit.ok(); ++lit) {
364  particleSum += (lit().*scalarQuantity)();
365  }
366  }
367  }
368 
369  return ParallelOps::sum(particleSum);
370 }
371 
372 template <typename P>
373 inline void
375  const std::function<bool(const P&)>& a_removeCriterion) noexcept
376 {
377  CH_TIME("ParticleOps::removeParticles(ParticleContainer<P>, std::function<bool(const P&)>)");
378 
379  for (int lvl = 0; lvl <= a_particles.getFinestLevel(); lvl++) {
380  const DisjointBoxLayout& dbl = a_particles.getGrids()[lvl];
381  const DataIterator& dit = dbl.dataIterator();
382 
383  const int nbox = dit.size();
384 
385 #pragma omp parallel for schedule(runtime)
386  for (int mybox = 0; mybox < nbox; mybox++) {
387  const DataIndex& din = dit[mybox];
388 
389  List<P>& particles = a_particles[lvl][din].listItems();
390 
391  for (ListIterator<P> lit(particles); lit.ok();) {
392  if (a_removeCriterion(lit())) {
393  particles.remove(lit);
394  }
395  else {
396  ++lit;
397  }
398  }
399  }
400  }
401 }
402 
403 template <typename P>
404 inline void
406  ParticleContainer<P>& a_srcParticles,
407  const std::function<bool(const P&)>& a_transferCrit) noexcept
408 {
409  CH_TIME("ParticleOps::transferParticles(ParticleContainer<P>, ParticleContainer<P>& std::function<bool(const P&)>)");
410 
411  CH_assert(a_dstParticles.getRealm() == a_srcParticles.getRealm());
412  CH_assert(a_dstParticles.getFinestLevel() == a_srcParticles.getFinestLevel());
413 
414  for (int lvl = 0; lvl <= a_srcParticles.getFinestLevel(); lvl++) {
415  const DisjointBoxLayout& dbl = a_srcParticles.getGrids()[lvl];
416  const DataIterator& dit = dbl.dataIterator();
417 
418  const int nbox = dit.size();
419 
420 #pragma omp parallel for schedule(runtime)
421  for (int mybox = 0; mybox < nbox; mybox++) {
422  const DataIndex& din = dit[mybox];
423 
424  List<P>& dstParticles = a_dstParticles[lvl][din].listItems();
425  List<P>& srcParticles = a_srcParticles[lvl][din].listItems();
426 
427  for (ListIterator<P> lit(srcParticles); lit.ok();) {
428  if (a_transferCrit(lit())) {
429  dstParticles.transfer(lit);
430  }
431  else {
432  ++lit;
433  }
434  }
435  }
436  }
437 }
438 
439 template <typename P>
440 inline void
441 ParticleOps::setData(ParticleContainer<P>& a_particles, const std::function<void(P&)>& a_functor) noexcept
442 {
443  CH_TIME("ParticleOps::setData(ParticleContainer<P>, std::function<void(P&)>)");
444 
445  for (int lvl = 0; lvl <= a_particles.getFinestLevel(); lvl++) {
446  const DisjointBoxLayout& dbl = a_particles.getGrids()[lvl];
447  const DataIterator& dit = dbl.dataIterator();
448 
449  const int nbox = dit.size();
450 
451 #pragma omp parallel for schedule(runtime)
452  for (int mybox = 0; mybox < nbox; mybox++) {
453  const DataIndex& din = dit[mybox];
454 
455  List<P>& particles = a_particles[lvl][din].listItems();
456 
457  for (ListIterator<P> lit(particles); lit.ok(); ++lit) {
458  a_functor(lit());
459  }
460  }
461  }
462 }
463 
464 template <typename P, Real& (P::*particleScalarField)()>
465 inline void
466 ParticleOps::setValue(ParticleContainer<P>& a_particles, const Real a_value) noexcept
467 {
468  CH_TIME("ParticleOps::setValue(ParticleContainer<P>, Real");
469 
470  for (int lvl = 0; lvl <= a_particles.getFinestLevel(); lvl++) {
471  const DisjointBoxLayout& dbl = a_particles.getGrids()[lvl];
472  const DataIterator& dit = dbl.dataIterator();
473 
474  const int nbox = dit.size();
475 
476 #pragma omp parallel for schedule(runtime)
477  for (int mybox = 0; mybox < nbox; mybox++) {
478  const DataIndex& din = dit[mybox];
479 
480  List<P>& particles = a_particles[lvl][din].listItems();
481 
482  for (ListIterator<P> lit(particles); lit.ok(); ++lit) {
483  P& p = lit();
484 
485  (p.*particleScalarField)() = a_value;
486  }
487  }
488  }
489 }
490 
491 template <typename P, RealVect& (P::*particleVectorField)()>
492 inline void
493 ParticleOps::setValue(ParticleContainer<P>& a_particles, const RealVect a_value) noexcept
494 {
495  CH_TIME("ParticleOps::setValue(ParticleContainer<P>, RealVect");
496 
497  for (int lvl = 0; lvl <= a_particles.getFinestLevel(); lvl++) {
498  const DisjointBoxLayout& dbl = a_particles.getGrids()[lvl];
499  const DataIterator& dit = dbl.dataIterator();
500 
501  const int nbox = dit.size();
502 
503 #pragma omp parallel for schedule(runtime)
504  for (int mybox = 0; mybox < nbox; mybox++) {
505  const DataIndex& din = dit[mybox];
506 
507  List<P>& particles = a_particles[lvl][din].listItems();
508 
509  for (ListIterator<P> lit(particles); lit.ok(); ++lit) {
510  P& p = lit();
511 
512  (p.*particleVectorField)() = a_value;
513  }
514  }
515  }
516 }
517 
518 #ifdef CH_MPI
519 template <typename P>
520 inline void
521 ParticleOps::scatterParticles(
522  std::map<std::pair<unsigned int, unsigned int>, List<P>>& a_receivedParticles,
523  std::vector<std::map<std::pair<unsigned int, unsigned int>, List<P>>>& a_sentParticles) noexcept
524 {
525  CH_TIME("ParticleOps::scatterParticles");
526 
527  const int numRanks = numProc();
528 
529  int mpiErr;
530 
531  CH_assert(a_sentParticles.size() == numProc());
532 
533  const size_t linearSize = P().size();
534 
535  std::vector<unsigned int> sendSizes(numProc(), 0);
536  std::vector<unsigned int> recvSizes(numProc(), 0);
537 
538  // Figure out the size of a_sentParticles[irank]
539  for (int irank = 0; irank < numRanks; irank++) {
540  sendSizes[irank] = 0;
541 
542  // Figure out the message size sent from this rank.
543  const std::map<std::pair<unsigned int, unsigned int>, List<P>>& particlesToRank = a_sentParticles[irank];
544 
545  // Size of particles along.
546  for (const auto& m : particlesToRank) {
547  sendSizes[irank] += m.second.length();
548  }
549  sendSizes[irank] *= linearSize;
550 
551  // Add in the map keys and the list length
552  sendSizes[irank] += particlesToRank.size() * (2 * sizeof(unsigned int) + sizeof(size_t));
553  }
554 
555  mpiErr = MPI_Alltoall(&sendSizes[0], 1, MPI_UNSIGNED, &recvSizes[0], 1, MPI_UNSIGNED, Chombo_MPI::comm);
556  if (mpiErr != MPI_SUCCESS) {
557  MayDay::Error("ParticleOps::scatterParticles - MPI communication error in MPI_AlltoAll");
558  }
559 
560  // Sanity check
561  if (recvSizes[procID()] != 0) {
562  MayDay::Error("ParticleOps::scatterParticles - 'recvSizes[procID()] != 0' failed");
563  }
564 
565  std::vector<MPI_Request> recvReq(numRanks - 1);
566  std::vector<char*> recvBuf(numRanks, nullptr);
567 
568  std::vector<MPI_Request> sendReq(numRanks - 1);
569  std::vector<char*> sendBuf(numRanks, nullptr);
570 
571  int recvReqCount = 0;
572  int sendReqCount = 0;
573 
574  for (int irank = 0; irank < numRanks; irank++) {
575  if (recvSizes[irank] > 0) {
576  recvBuf[irank] = new char[recvSizes[irank]];
577 
578  MPI_Irecv(recvBuf[irank], recvSizes[irank], MPI_CHAR, irank, 1, Chombo_MPI::comm, &recvReq[recvReqCount]);
579 
580  recvReqCount++;
581  }
582  }
583 
584  // Pack data into buffers
585  for (int irank = 0; irank < numRanks; irank++) {
586  if (sendSizes[irank] > 0) {
587  sendBuf[irank] = new char[sendSizes[irank]];
588 
589  char* data = sendBuf[irank];
590 
591  const std::map<std::pair<unsigned int, unsigned int>, List<P>>& particles = a_sentParticles[irank];
592 
593  // Linearize each entry in the map onto the send buffer. We encode this by (level, index, list length, List<P>)
594  for (const auto& cur : particles) {
595 
596  // and grid index
597  *((unsigned int*)data) = cur.first.first;
598  data += sizeof(unsigned int);
599  *((unsigned int*)data) = cur.first.second;
600  data += sizeof(unsigned int);
601 
602  // List length aka number of particles
603  *((size_t*)data) = cur.second.length();
604  data += sizeof(size_t);
605 
606  // Linearize the particle list
607  for (ListIterator<P> lit(cur.second); lit.ok(); ++lit) {
608  const P& p = lit();
609 
610  p.linearOut((void*)data);
611  data += linearSize;
612  }
613  }
614 
615  // Send from me to the other rank.
616  MPI_Isend(sendBuf[irank], sendSizes[irank], MPI_CHAR, irank, 1, Chombo_MPI::comm, &sendReq[sendReqCount]);
617 
618  sendReqCount++;
619  }
620  }
621 
622  std::vector<MPI_Status> recvStatus(numRanks - 1);
623  std::vector<MPI_Status> sendStatus(numRanks - 1);
624 
625  if (sendReqCount > 0) {
626  mpiErr = MPI_Waitall(sendReqCount, &sendReq[0], &sendStatus[0]);
627 
628  if (mpiErr != MPI_SUCCESS) {
629  MayDay::Error("ParticleOps::scatterParticles: send communication failed");
630  }
631  }
632 
633  if (recvReqCount > 0) {
634  mpiErr = MPI_Waitall(recvReqCount, &recvReq[0], &recvStatus[0]);
635 
636  if (mpiErr != MPI_SUCCESS) {
637  MayDay::Error("ParticleOps::scatterParticles: receive communication failed");
638  }
639  }
640 
641  // Unpack the send buffers
642  for (int irank = 0; irank < numRanks; irank++) {
643  P p;
644 
645  if (recvSizes[irank] > 0) {
646 
647  char* data = recvBuf[irank];
648 
649  unsigned int in = 0;
650 
651  while (in < recvSizes[irank]) {
652 
653  // Level and grid index
654  unsigned int lvl = *((unsigned int*)data);
655  data += sizeof(unsigned int);
656 
657  unsigned int idx = *((unsigned int*)data);
658  data += sizeof(unsigned int);
659 
660  // List length aka number of particles
661  size_t numParticles = *((size_t*)data);
662  data += sizeof(size_t);
663 
664  for (size_t ipart = 0; ipart < numParticles; ipart++) {
665  p.linearIn((void*)data);
666  data += linearSize;
667 
668  a_receivedParticles[std::pair<unsigned int, unsigned int>(lvl, idx)].add(p);
669  }
670 
671  in += 2 * sizeof(unsigned int) + sizeof(size_t) + numParticles * linearSize;
672  }
673  }
674  }
675 
676  // Delete buffers
677  for (int irank = 0; irank < numRanks; irank++) {
678  a_sentParticles[irank].clear();
679 
680  if (sendSizes[irank] > 0) {
681  delete[] sendBuf[irank];
682  }
683  if (recvSizes[irank] > 0) {
684  delete[] recvBuf[irank];
685  }
686  }
687 }
688 #endif
689 
690 #include <CD_NamespaceFooter.H>
691 
692 #endif
Declaration of a static class containing some common useful particle routines that would otherwise be...
Agglomeration of some useful algebraic/polynomial routines.
File containing some useful static methods related to random number generation.
Templated class for holding particles on an AMR hierarchy with particle remapping.
Definition: CD_ParticleContainer.H:50
const Vector< DisjointBoxLayout > & getGrids() const
Get the AMR grids.
Definition: CD_ParticleContainerImplem.H:249
int getFinestLevel() const
Get finest AMR level.
Definition: CD_ParticleContainerImplem.H:231
const std::string getRealm() const
Get the realm where this ParticleContainer lives.
Definition: CD_ParticleContainerImplem.H:240
static void copy(ParticleContainer< P > &a_dst, const ParticleContainer< P > &a_src) noexcept
Copy all the particles from the a_src to a_dst.
Definition: CD_ParticleOpsImplem.H:268
static Real sum(const ParticleContainer< P > &a_particles) noexcept
Perform a sum of some particle quantity.
Definition: CD_ParticleOpsImplem.H:316
static void getComputationalParticlesPerCell(EBAMRCellData &a_ppc, const ParticleContainer< P > &a_src) noexcept
Get the number of computational particles per cell.
Definition: CD_ParticleOpsImplem.H:83
static void transferParticles(ParticleContainer< P > &a_dstParticles, ParticleContainer< P > &a_srcParticles, const std::function< bool(const P &)> &a_transferCrit) noexcept
Transfer particles if they fulfill a certain removal criterion. Takes particles from a_srcParticles a...
Definition: CD_ParticleOpsImplem.H:405
static bool ebIntersectionRaycast(const RefCountedPtr< BaseIF > &a_impFunc, const RealVect &a_oldPos, const RealVect &a_newPos, const Real &a_tolerance, Real &a_s)
Compute the intersection point between a particle path and an implicit function using a ray-casting a...
Definition: CD_ParticleOpsImplem.H:217
static bool ebIntersectionBisect(const RefCountedPtr< BaseIF > &a_impFunc, const RealVect &a_oldPos, const RealVect &a_newPos, const Real &a_bisectStep, Real &a_s)
Compute the intersection point between a particle path and an implicit function using a bisection alg...
Definition: CD_ParticleOpsImplem.H:173
static void setValue(ParticleContainer< P > &a_particles, const Real a_value) noexcept
Set value function. Lets the user set scalar particle quantity. Use with e.g. setValue<P,...
Definition: CD_ParticleOpsImplem.H:466
static void removeParticles(ParticleContainer< P > &a_particles, const std::function< bool(const P &)> &a_removeCriterion) noexcept
Remove particles if they fulfill certain removal criterion.
Definition: CD_ParticleOpsImplem.H:374
static void getPhysicalParticlesPerCell(EBAMRCellData &a_ppc, const ParticleContainer< P > &a_src) noexcept
Get the number of physical particles per cell.
Definition: CD_ParticleOpsImplem.H:47
static void copyDestructive(ParticleContainer< P > &a_dst, ParticleContainer< P > &a_src) noexcept
Copy all the particles from the a_src to a_dst. This destroys the source particles.
Definition: CD_ParticleOpsImplem.H:291
static IntVect getParticleGridCell(const RealVect &a_particlePosition, const RealVect &a_probLo, const RealVect &a_dx) noexcept
Get the grid cell where the particle lives.
Definition: CD_ParticleOpsImplem.H:117
static IntVect getParticleCellIndex(const RealVect &a_particlePosition, const RealVect &a_probLo, const Real &a_dx) noexcept
Get the cell index corresponding to the particle position.
Definition: CD_ParticleOpsImplem.H:26
static void setData(ParticleContainer< P > &a_particles, const std::function< void(P &)> &a_functor) noexcept
Set value function. Lets the user set particle parameters.
Definition: CD_ParticleOpsImplem.H:441
static bool domainIntersection(const RealVect &a_oldPos, const RealVect &a_newPos, const RealVect &a_probLo, const RealVect &a_probHi, Real &a_s)
Compute the intersection point between a particle path and a domain side.
Definition: CD_ParticleOpsImplem.H:127
Real max(const Real &a_input) noexcept
Get the maximum of the input, reduced over MPI ranks (in the Chombo communicator)
Definition: CD_ParallelOpsImplem.H:176
Real sum(const Real &a_value) noexcept
Compute the sum across all MPI ranks.
Definition: CD_ParallelOpsImplem.H:353
RealVect brentRootFinder(const RefCountedPtr< BaseIF > &a_impFunc, const RealVect &a_point1, const RealVect &a_point2)
Compute the root of a function between two points. This is a 1D problem along the line.
Definition: CD_PolyUtils.cpp:20