chombo-discharge
Loading...
Searching...
No Matches
CD_ParticleContainerImplem.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_PARTICLECONTAINERIMPLEM_H
14#define CD_PARTICLECONTAINERIMPLEM_H
15
16// Std includes
17#include <bitset>
18
19// Chombo includes
20#include <ParmParse.H>
21#include <CH_Timer.H>
22#include <MPI_util.H>
23#include <ParticleDataI.H>
24
25// Our includes
27#include <CD_ParallelOps.H>
28#include <CD_ParticleOps.H>
29#include <CD_Timer.H>
30#include <CD_BoxLoops.H>
31#include <CD_OpenMP.H>
32#include <CD_NamespaceHeader.H>
33
34template <class P>
36
37template <class P>
40 const Vector<Real>& a_dx,
41 const Vector<int>& a_refRat,
44 const RealVect& a_probLo,
45 const int a_blockingFactor,
46 const int a_finestLevel,
47 const std::string a_realm)
48{
49 CH_TIME("ParticleContainer::ParticleContainer");
50
51 this->define(a_grids,
53 a_dx,
60 a_realm);
61}
62
63template <class P>
65{
66 CH_TIME("ParticleContainer::~ParticleContainer");
67}
68
69template <class P>
70void
73 const Vector<Real>& a_dx,
74 const Vector<int>& a_refRat,
77 const RealVect& a_probLo,
78 const int a_blockingFactor,
79 const int a_finestLevel,
80 const std::string& a_realm)
81{
82 CH_TIME("ParticleContainer::~ParticleContainer");
83
84 m_grids = a_grids;
85 m_domains = a_domains;
86 m_refRat = a_refRat;
87 m_validRegion = a_validMask;
88 m_probLo = a_probLo;
89 m_finestLevel = a_finestLevel;
91 m_blockingFactor = a_blockingFactor;
92 m_levelTiles = a_levelTiles;
93
94 m_dx.resize(1 + m_finestLevel);
95 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
96 m_dx[lvl] = a_dx[lvl] * RealVect::Unit;
97 }
98
99 constexpr int base = 0;
100
101 // Do the define stuff.
102 this->setupGrownGrids(base, m_finestLevel);
103 this->setupParticleData(base, m_finestLevel);
104
105 m_isDefined = true;
106 m_isOrganizedByCell = false;
107 m_profile = false;
108
109 ParmParse pp("ParticleContainer");
110 pp.query("profile", m_profile);
111 pp.query("debug", m_debug);
112 pp.query("verbose", m_verbose);
113}
114
115template <class P>
116void
118{
119 CH_TIME("ParticleContainer::setupGrownGrids");
120 if (m_verbose) {
121 pout() << "ParticleContainer::setupGrownGrids" << endl;
122 }
123
124 // TLDR: This routine sets up the buffer grids which can be used when we need to fetch particles that fall off each levels' grid. This is very useful
125 // when we want to fetch particles that lie on the coarse grid of a refinement boundary.
126
127 m_grownGrids.resize(1 + a_finestLevel);
128
129 for (int lvl = 0; lvl <= a_finestLevel; lvl++) {
130 const DisjointBoxLayout& dbl = m_grids[lvl];
131 const ProblemDomain& domain = m_domains[lvl];
132
133 // Grow boxes by refinement factor on the finer levels.
134 Vector<Box> boxes = dbl.boxArray();
135 if (lvl > 0) {
136 for (auto& box : boxes.stdVector()) {
137 box.grow(m_refRat[lvl - 1]);
138 box &= domain;
139 }
140 }
141
142 m_grownGrids[lvl] = BoxLayout(boxes, dbl.procIDs());
143 }
145
146template <class P>
147void
149{
150 CH_TIME("ParticleContainer::setupParticleData");
151 if (m_verbose) {
152 pout() << "ParticleContainer::setupParticleData" << endl;
153 }
154
155 // TLDR: This sets up the most commonly used particle data holders for this particle AMR container. This means that
156 // we allocate:
157 //
158 // 1. The "normal" particle container data holder m_particles. This is defined on the DisjointBoxLayout
159 // which is the natural place for the particles to live.
160 //
161 // 2. A buffer particle data holder which is defined on a grown DisjointBoxLayout. This is useful when particles
162 // on the coarse level need to deposit to the fine level.
163 //
164 // 3. A data holder for "masked particles", providing an opportunity to copy/transfer some of the particles on a grid
165 // level to a separate data holder if they lie within a "mask". Typically used for extracting coarse-level that live
166 // just outside the fine grid (i.e. on the coarse side of the refinement boundary).
167 //
168 // 5. A data holder for storing cell-sorted particles. Very useful when particles need to be sorted by cell rather than patch.
169
170 m_particles.resize(1 + a_finestLevel);
171 m_bufferParticles.resize(1 + a_finestLevel);
172 m_maskParticles.resize(1 + a_finestLevel);
173 m_cellSortedParticles.resize(1 + a_finestLevel);
174
175 for (int lvl = a_base; lvl <= a_finestLevel; lvl++) {
177 new ParticleData<P>(m_grids[lvl], m_domains[lvl], m_blockingFactor, m_dx[lvl], m_probLo));
178
179 m_bufferParticles[lvl] = RefCountedPtr<ParticleData<P>>(
180 new ParticleData<P>(m_grownGrids[lvl], m_domains[lvl], m_blockingFactor, m_dx[lvl], m_probLo));
181
182 m_maskParticles[lvl] = RefCountedPtr<ParticleData<P>>(
183 new ParticleData<P>(m_grids[lvl], m_domains[lvl], m_blockingFactor, m_dx[lvl], m_probLo));
184
185 m_cellSortedParticles[lvl] = RefCountedPtr<LayoutData<BinFab<P>>>(new LayoutData<BinFab<P>>(m_grids[lvl]));
186 }
187}
189template <class P>
190void
192{
193 CH_TIME("ParticleContainer::sortParticles");
194 if (m_verbose) {
195 pout() << "ParticleContainer::sortParticles" << endl;
197
198 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
199
200 const DisjointBoxLayout& dbl = m_grids[lvl];
201 const DataIterator& dit = dbl.dataIterator();
203 const int nbox = dit.size();
204
205#pragma omp parallel for schedule(runtime)
206 for (int mybox = 0; mybox < nbox; mybox++) {
207 const DataIndex& din = dit[mybox];
210
211 particles.sort();
212 }
213 }
215
216template <class P>
217bool
219{
220 return m_isOrganizedByCell;
221}
222
223template <class P>
224int
227 CH_assert(m_isDefined);
228
229 return m_finestLevel;
230}
231
232template <class P>
235{
236 CH_assert(m_isDefined);
237
238 return m_realm;
239}
241template <class P>
244{
245 return m_grids;
246}
248template <class P>
251{
252 return m_probLo;
253}
255template <class P>
258{
259 return m_dx;
260}
262template <class P>
265{
266 CH_assert(m_isDefined);
267
268 if (m_isOrganizedByCell) {
269 MayDay::Error("ParticleContainer::getParticles - particles are sorted by cell!");
270 }
271
272 return m_particles;
273}
274
275template <class P>
276const AMRParticles<P>&
278{
279 CH_assert(m_isDefined);
280
281 if (m_isOrganizedByCell) {
282 MayDay::Abort("ParticleContainer::getParticles - particles are sorted by cell!");
283 }
284
285 return m_particles;
286}
287
288template <class P>
291{
292 CH_assert(m_isDefined);
293
294 return m_bufferParticles;
295}
297template <class P>
298const AMRParticles<P>&
300{
301 CH_assert(m_isDefined);
302
303 return m_bufferParticles;
304}
305
306template <class P>
309{
310 CH_assert(m_isDefined);
311
312 return m_maskParticles;
313}
314
315template <class P>
316const AMRParticles<P>&
318{
319 CH_assert(m_isDefined);
320
321 return m_maskParticles;
322}
323
324template <class P>
327{
328 CH_assert(m_isDefined);
329
330 if (m_isOrganizedByCell) {
331 MayDay::Error("ParticleContainer::operator[](a_lvl) - particles are sorted by cell!");
332 }
334 return *m_particles[a_lvl];
335}
336
337template <class P>
338const ParticleData<P>&
340{
341 CH_assert(m_isDefined);
342
343 if (m_isOrganizedByCell) {
344 MayDay::Error("ParticleContainer::operator[](a_lvl) - particles are sorted by cell!");
345 }
346
347 return *m_particles[a_level];
348}
350template <class P>
353{
354 CH_assert(m_isDefined);
355
356 if (!m_isOrganizedByCell) {
357 MayDay::Error("ParticleContainer::getCellParticles()- particles are not sorted by cell!");
359
360 return m_cellSortedParticles;
361}
362
363template <class P>
366{
367 CH_assert(m_isDefined);
368
369 if (!m_isOrganizedByCell) {
370 MayDay::Error("ParticleContainer::getCellParticles()- particles are not sorted by cell!");
371 }
372
373 return m_cellSortedParticles;
374}
375
376template <class P>
379{
380 CH_assert(m_isDefined);
381
382 if (!m_isOrganizedByCell) {
383 MayDay::Error("ParticleContainer::getCellParticles(level)- particles are not sorted by cell!");
384 }
385
386 return *m_cellSortedParticles[a_level];
388
389template <class P>
392{
393 CH_TIME("ParticleContainer::getCellParticles(int)");
394
395 CH_assert(m_isDefined);
396
397 if (!m_isOrganizedByCell) {
398 MayDay::Error("ParticleContainer::getCellParticles(level)- particles are not sorted by cell!");
399 }
400
401 return *m_cellSortedParticles[a_level];
402}
403
404template <class P>
405void
408 CH_TIME("ParticleContainer::getCellParticles(BinFab)");
409
410 CH_assert(m_isDefined);
411
412 if (!m_isOrganizedByCell) {
413 MayDay::Error("ParticleContainer::getCellParticles - particles are not sorted by cell!");
415
416 cellParticles.define(m_grids[a_lvl][a_dit], m_dx[a_lvl], m_probLo);
418}
419
420template <class P>
421void
423{
424 CH_TIME("ParticleContainer::getCellParticlesDestructive");
425
426 CH_assert(m_isDefined);
427
428 if (!m_isOrganizedByCell) {
429 MayDay::Error("ParticleContainer::getCellParticlesDestructive - particles are not sorted by cell!");
430 }
431
432 cellParticles.define(m_grids[a_lvl].get(a_dit), m_dx[a_lvl], m_probLo);
433 cellParticles.addItemsDestructive((*m_particles[a_lvl])[a_dit].listItems());
434}
436template <class P>
439{
440 CH_TIME("ParticleContainer::getCellParticles(int, DataIndex)");
441
442 CH_assert(m_isDefined);
443
444 if (!m_isOrganizedByCell) {
445 MayDay::Error("ParticleContainer::getCellParticles(int, dit) - particles are not sorted by cell!");
446 }
447
448 return (*m_cellSortedParticles[a_level])[a_dit];
449}
450
451template <class P>
452const BinFab<P>&
454{
455 CH_TIME("ParticleContainer::getCellParticles(int, DataIndex)");
456
457 CH_assert(m_isDefined);
458
459 if (!m_isOrganizedByCell) {
460 MayDay::Error("ParticleContainer::getCellParticles(int, dit) - particles are not sorted by cell!");
461 }
462
463 return (*m_cellSortedParticles[a_level])[a_dit];
464}
465
466template <class P>
467void
469{
470 CH_TIME("ParticleContainer::organizeParticlesByCell");
471 if (m_verbose) {
472 pout() << "ParticleContainer::organizeParticlesByCell" << endl;
474
475 CH_assert(m_isDefined);
476
477 if (!m_isOrganizedByCell) {
478
479 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
480 const DisjointBoxLayout& dbl = m_grids[lvl];
481 const DataIterator& dit = dbl.dataIterator();
482
483 const int nbox = dit.size();
484
485#pragma omp parallel for schedule(runtime)
486 for (int mybox = 0; mybox < nbox; mybox++) {
488
489 BinFab<P>& cellParticles = (*m_cellSortedParticles[lvl])[din];
490
491 cellParticles.define(dbl[din], m_dx[lvl], m_probLo);
492 cellParticles.addItemsDestructive((*m_particles[lvl])[din].listItems());
493 }
494 }
496 m_isOrganizedByCell = true;
497 }
498}
499
500template <class P>
501void
503{
504 CH_TIME("ParticleContainer::organizeParticlesByPatch");
505 if (m_verbose) {
506 pout() << "ParticleContainer::organizeParticlesByPatch" << endl;
507 }
508
509 CH_assert(m_isDefined);
510
511 if (m_isOrganizedByCell) {
512
513 constexpr int comp = 0;
514
515 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
516 const DisjointBoxLayout& dbl = m_grids[lvl];
517 const DataIterator& dit = dbl.dataIterator();
518
519 const int nbox = dit.size();
520
521#pragma omp parallel for schedule(runtime)
522 for (int mybox = 0; mybox < nbox; mybox++) {
523 const DataIndex& din = dit[mybox];
524
526 BinFab<P>& cellParticles = (*m_cellSortedParticles[lvl])[din];
527
528 // Kernel which adds moves particles from the cell container to the patch container.
529 auto kernel = [&](const IntVect& iv) -> void {
530 patchParticles.addItemsDestructive(cellParticles(iv, comp));
531 };
532
533 // Not vectorizable: per-cell linked-list splice (List<P> pointer surgery) with a loop-carried
534 // dependency on the shared patchParticles accumulator. Not a numerical kernel.
535 BoxLoops::loop<D_DECL(1, 1, 1)>(dbl[din], kernel);
536
537 cellParticles.clear();
538 }
539 }
541 m_isOrganizedByCell = false;
542 }
543}
544
545template <class P>
546void
548{
549 CH_TIME("ParticleContainer::addParticles");
550 if (m_verbose) {
551 pout() << "ParticleContainer::addParticles" << endl;
552 }
553
554 CH_assert(m_isDefined);
555
556 if (m_isOrganizedByCell) {
557 MayDay::Error("ParticleContainer::addParticles(List<P>) - particles are sorted by cell!");
558 }
559
560 // TLDR: This routine is almost a precise copy of the remap routine, with the exception that the particles to be remapped
561 // are only the input particles rather than everything inside m_particles
562
563 const uint32_t numRanks = numProc();
564 const uint32_t myRank = procID();
565
567
569
570#pragma omp parallel
571 {
573
574#pragma omp master
575 {
576 outcasts.join(a_particles);
577 }
578
580
581 // Collect particles owned by this rank/thread.
582 this->transferParticlesToSingleList(outcasts, m_particles);
583
584 // Map the particles to other grid patches
585 this->mapParticlesToAMRGrid(threadLocalParticlesToSend, outcasts);
586
587 // Catenate the thread-local particle lists onto particlesToSend
588#pragma omp critical
589 this->catenateParticleMaps(particlesToSend, threadLocalParticlesToSend);
590 }
591
592 // Particles that go from this rank to this rank don't need to be communicated so that we can place them directly on the correct patch.
593 this->assignLocalParticles(particlesToSend[myRank], m_particles);
594
595 // If using MPI then we have to scatter the particles across MPI ranks.
596#ifdef CH_MPI
598
599 ParticleOps::scatterParticles(receivedParticles, particlesToSend);
600
601 // Assign particles to the correct level and grid patch -- we iterate through receivedParticles and decode the information
602 // we got from there.
603 this->assignLocalParticles(receivedParticles, m_particles);
604#endif
605
606 if (m_debug) {
607 this->sanityCheck();
608 }
609}
610
611template <class P>
612void
614{
615 CH_TIME("ParticleContainer::addParticlesDestructive");
616 if (m_verbose) {
617 pout() << "ParticleContainer::addParticlesDestructive" << endl;
618 }
619
620 CH_assert(m_isDefined);
621
622 if (m_isOrganizedByCell) {
623 MayDay::Error("ParticleContainer::addParticlesDestructive(List<P>) - particles are sorted by cell!");
624 }
625
626 // TLDR: This routine is almost a precise copy of the remap routine, with the exception that the particles to be remapped
627 // are only the input particles rather than everything inside m_particles
628
629 const uint32_t numRanks = numProc();
630 const uint32_t myRank = procID();
631
633
635
636#pragma omp parallel
637 {
639
640#pragma omp master
641 {
642 outcasts.catenate(a_particles);
643 }
644
646
647 // Collect particles owned by this rank/thread.
648 this->transferParticlesToSingleList(outcasts, m_particles);
649
650 // Map the particles to other grid patches
651 this->mapParticlesToAMRGrid(threadLocalParticlesToSend, outcasts);
652
653 // Catenate the thread-local particle lists onto particlesToSend
654#pragma omp critical
655 this->catenateParticleMaps(particlesToSend, threadLocalParticlesToSend);
656 }
657
658 // Particles that go from this rank to this rank don't need to be communicated so that we can place them directly on the correct patch.
659 this->assignLocalParticles(particlesToSend[myRank], m_particles);
660
661 // If using MPI then we have to scatter the particles across MPI ranks.
662#ifdef CH_MPI
664
665 ParticleOps::scatterParticles(receivedParticles, particlesToSend);
666
667 // Assign particles to the correct level and grid patch -- we iterate through receivedParticles and decode the information
668 // we got from there.
669 this->assignLocalParticles(receivedParticles, m_particles);
670#endif
671
672 if (m_debug) {
673 this->sanityCheck();
674 }
675}
676
677template <class P>
678void
680{
681 CH_TIME("ParticleContainer::addParticles(BinFab)");
682 if (m_verbose) {
683 pout() << "ParticleContainer::addParticles(BinFab)" << endl;
684 }
685
686 CH_assert(m_isDefined);
687 CH_assert(m_grids[a_lvl].get(a_dit) == a_particles.getRegion());
688
689 if (!m_isOrganizedByCell) {
690 MayDay::Abort("ParticleContainer::addParticles(BinFab<P>) - particles are not sorted by cell!");
691 }
692
693 constexpr int comp = 0;
694
695 BinFab<P>& boxParticles = (*m_cellSortedParticles[a_lvl])[a_dit];
696
697 auto kernel = [&](const IntVect& iv) -> void {
700
702 };
703
704 // Not vectorizable: per-cell linked-list join (List<P> pointer surgery). Not a numerical kernel.
705 BoxLoops::loop<D_DECL(1, 1, 1)>(m_grids[a_lvl][a_dit], kernel);
706}
707
708template <class P>
709void
711{
712 CH_TIME("ParticleContainer::addParticlesDestructive(BinFab)");
713 if (m_verbose) {
714 pout() << "ParticleContainer::addParticlesDestructive(BinFab)" << endl;
715 }
716
717 CH_assert(m_isDefined);
718 CH_assert(m_grids[a_lvl].get(a_dit) == a_particles.getRegion());
719
720 if (!m_isOrganizedByCell) {
721 MayDay::Error("ParticleContainer::addParticles(BinFab<P>) - particles are not sorted by cell!");
722 }
724 constexpr int comp = 0;
725
726 BinFab<P>& boxParticles = (*m_cellSortedParticles[a_lvl])[a_dit];
727
728 auto kernel = [&](const IntVect& iv) -> void {
732 myParticles.catenate(inputParticles);
733 };
734
735 // Not vectorizable: per-cell linked-list catenate (List<P> pointer surgery). Not a numerical kernel.
736 BoxLoops::loop<D_DECL(1, 1, 1)>(m_grids[a_lvl][a_dit], kernel);
737}
738
739template <class P>
740void
742{
743 CH_TIME("ParticleContainer::addParticles(ParticleContainer)");
744 if (m_verbose) {
745 pout() << "ParticleContainer::addParticles(ParticleContainer)" << endl;
746 }
747
748 CH_assert(m_isDefined);
750 if (m_isOrganizedByCell) {
751 MayDay::Error("ParticleContainer::addParticles(ParticleContainer<P>) - particles are sorted by cell!");
752 }
753
754 // TLDR: This routine adds particles from a different container to this container. If the containers live on the same realm
755 // we just add the particles directly. Otherwise, we have to add to the outcast list and remap.
756
757 if (m_realm == a_otherContainer.getRealm()) {
758 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
759 const DisjointBoxLayout& dbl = m_grids[lvl];
760 const DataIterator& dit = dbl.dataIterator();
761
762 const int nbox = dit.size();
763
764#pragma omp parallel for schedule(runtime)
765 for (int mybox = 0; mybox < nbox; mybox++) {
766 const DataIndex& din = dit[mybox];
767
770
771 myParticles.addItems(otherParticles.listItems());
772 }
773 }
774 }
775 else {
776 // If we're adding particles across realms we need to collect the input particles onto lists that get remapped
777 // into m_particles. A full explanation of how this works is given in the remap routine.
778
779 const uint32_t numRanks = numProc();
780 const uint32_t myRank = procID();
781
783
785
786#pragma omp parallel
787 {
789
791
792 // Collect particles owned by this rank/thread, but on the other realm.
793 a_otherContainer.copyParticlesToSingleList(outcasts, a_otherContainer.getParticles());
794
795 // Map the particles to other grid patches
796 this->mapParticlesToAMRGrid(threadLocalParticlesToSend, outcasts);
797
798 // Catenate the thread-local particle lists onto particlesToSend
799#pragma omp critical
800 this->catenateParticleMaps(particlesToSend, threadLocalParticlesToSend);
801 }
802
803 // Particles that go from this rank to this rank don't need to be communicated so that we can place them directly on the correct patch.
804 this->assignLocalParticles(particlesToSend[myRank], m_particles);
805
806 // If using MPI then we have to scatter the particles across MPI ranks.
807#ifdef CH_MPI
809
810 ParticleOps::scatterParticles(receivedParticles, particlesToSend);
811
812 // Assign particles to the correct level and grid patch -- we iterate through receivedParticles and decode the information
813 // we got from there.
814 this->assignLocalParticles(receivedParticles, m_particles);
815#endif
816 }
817
818 if (m_debug) {
819 this->sanityCheck();
820 }
821}
822
823template <class P>
824void
826{
827 CH_TIME("ParticleContainer::addParticlesDestructive(ParticleContainer)");
828 if (m_verbose) {
829 pout() << "ParticleContainer::addParticlesDestructive(ParticleContainer)" << endl;
830 }
831
832 // TLDR: This routine adds particles from a different container to this container. If the containers live on the same realm
833 // we just add the particles directly. Otherwise, we have to add to the outcast list and remap.
834
835 CH_assert(m_isDefined);
836
837 if (m_isOrganizedByCell) {
838 MayDay::Error("ParticleContainer::addParticles(ParticleContainer<P>) - particles are sorted by cell!");
839 }
840
841 // TLDR: This routine adds particles from a different container to this container. If the containers live on the same realm
842 // we just add the particles directly. Otherwise, we have to add to the outcast list and remap.
843
844 if (m_realm == a_otherContainer.getRealm()) {
845 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
846 const DisjointBoxLayout& dbl = m_grids[lvl];
847 const DataIterator& dit = dbl.dataIterator();
848
849 const int nbox = dit.size();
850
851#pragma omp parallel for schedule(runtime)
852 for (int mybox = 0; mybox < nbox; mybox++) {
853 const DataIndex& din = dit[mybox];
854
857
858 myParticles.addItems(otherParticles.listItems());
859 }
860 }
861 }
862 else {
863 // If we're adding particles across realms we need to collect the input particles onto lists that get remapped
864 // into m_particles. A full explanation of how this works is given in the remap routine.
865
866 const uint32_t numRanks = numProc();
867 const uint32_t myRank = procID();
868
870
872
873#pragma omp parallel
874 {
876
878
879 // Collect particles owned by this rank/thread, but on the other realm.
880 a_otherContainer.transferParticlesToSingleList(outcasts, a_otherContainer.getParticles());
881
882 // Map the particles to other grid patches
883 this->mapParticlesToAMRGrid(threadLocalParticlesToSend, outcasts);
884
885 // Catenate the thread-local particle lists onto particlesToSend
886#pragma omp critical
887 this->catenateParticleMaps(particlesToSend, threadLocalParticlesToSend);
888 }
889
890 // Particles that go from this rank to this rank don't need to be communicated so that we can place them directly on the correct patch.
891 this->assignLocalParticles(particlesToSend[myRank], m_particles);
892
893 // If using MPI then we have to scatter the particles across MPI ranks.
894#ifdef CH_MPI
896
897 ParticleOps::scatterParticles(receivedParticles, particlesToSend);
898
899 // Assign particles to the correct level and grid patch -- we iterate through receivedParticles and decode the information
900 // we got from there.
901 this->assignLocalParticles(receivedParticles, m_particles);
902#endif
903 }
904
905 if (m_debug) {
906 this->sanityCheck();
907 }
908}
909
910template <class P>
911void
913{
914 CH_TIME("ParticleContainer::transferParticles");
915
916 CH_assert(m_isDefined);
917 CH_assert(this->getRealm() == a_otherContainer.getRealm());
918
919 if (m_isOrganizedByCell) {
920 MayDay::Error("ParticleContainer::transferParticles(ParticleContainer<P>) - particles are sorted by cell!");
921 }
922
923 if (a_otherContainer.isOrganizedByCell()) {
924 MayDay::Error("ParticleContainer::transferParticles(ParticleContainer<P>) - other particles are sorted by cell!");
925 }
926
927 if (a_otherContainer.getRealm() != m_realm) {
928 MayDay::Error(
929 "ParticleContainer::transferParticles(ParticleContainer<P>) - other container defined over a different realm");
930 }
931
932 this->transferParticles(a_otherContainer.getParticles());
933}
934
935template <class P>
936void
938{
939 CH_TIME("ParticleContainer::transferParticles(AMRParticles)");
940 if (m_verbose) {
941 pout() << "ParticleContainer::transferParticle(AMRParticles)" << endl;
942 }
943
944 CH_assert(m_isDefined);
945
946 if (m_isOrganizedByCell) {
947 MayDay::Error("ParticleContainer::transferParticles(ParticleContainer<P>) - particles are sorted by cell!");
948 }
949
950 // Ok, transfer the particles.
951 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
952 const DisjointBoxLayout& dbl = m_grids[lvl];
953 const DataIterator& dit = dbl.dataIterator();
954
955 const int nbox = dit.size();
956
957#pragma omp parallel for schedule(runtime)
958 for (int mybox = 0; mybox < nbox; mybox++) {
959 const DataIndex& din = dit[mybox];
960
963
964 myParticles.catenate(otherParticles);
965 }
966 }
967}
968
969template <class P>
970void
972{
973 CH_TIME("ParticleContainer<P>::remap");
974 if (m_verbose) {
975 pout() << "ParticleContainer::remap" << endl;
976 }
977
978 // TLDR: This routine is quite long but does the full remapping on the whole hierarchy. It will discard particles that fall off the grid.
979 //
980 // This is done in the following steps:
981 // 1) Collect all particles from this rank onto thread-local variables
982 // 2) Iterate through those particles and figure out where they end up up the AMR hierarchy (level, grid index, and ownership, i.e. the MPI rank)
983 // 3) Collect those particles onto a rank-only (no thread local) variable
984 // 4) Assign particles _locally_, i.e. assign particles sent from this rank to this rank directly onto m_particles
985 // 5) If using MPI, scatter the particles to the appropriate ranks.
986 // 6) Assign particles locally from the particles that were scattered to this rank.
987
988 const uint32_t numRanks = numProc();
989 const uint32_t myRank = procID();
990
992
993 // These are the particles that get sent to each MPI rank. The pair contains the grid level and grid index.
994 // Each MPI rank (and OpenMP thread) goes through it's particles and checks if they've fallen off the grid. If they have,
995 // then the particles are moved onto appropriate lists that will get sent to each rank.
997
998#pragma omp parallel
999 {
1001
1003
1004 // Collect particles owned by this rank/thread.
1005 this->transferParticlesToSingleList(outcasts, m_particles);
1006
1007 // Map the particles to other grid patches
1008 this->mapParticlesToAMRGrid(threadLocalParticlesToSend, outcasts);
1009
1010 // Catenate the thread-local particle lists onto particlesToSend
1011#pragma omp critical
1012 this->catenateParticleMaps(particlesToSend, threadLocalParticlesToSend);
1013 }
1014
1015 // Particles that go from this rank to this rank don't need to be communicated so that we can place them directly on the correct patch.
1016 this->assignLocalParticles(particlesToSend[myRank], m_particles);
1017
1018 // If using MPI then we have to scatter the particles across MPI ranks.
1019#ifdef CH_MPI
1021
1022 ParticleOps::scatterParticles(receivedParticles, particlesToSend);
1023
1024 // Assign particles to the correct level and grid patch -- we iterate through receivedParticles and decode the information
1025 // we got from there.
1026 this->assignLocalParticles(receivedParticles, m_particles);
1027#endif
1028
1029 if (m_debug) {
1030 this->sanityCheck();
1031 }
1032}
1033
1034template <typename P>
1035void
1037{
1038 CH_TIME("ParticleContainer::sanityCheck");
1039
1040 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
1041 const DisjointBoxLayout& dbl = m_grids[lvl];
1042 const RealVect dx = m_dx[lvl];
1043
1044 for (DataIterator dit(dbl); dit.ok(); ++dit) {
1045 const List<P>& particles = (*m_particles[lvl])[dit()].listItems();
1046 const Box box = dbl[dit()];
1047
1048 for (ListIterator<P> lit(particles); lit.ok(); ++lit) {
1049 const RealVect particlePosition = lit().position();
1051
1052 if (!(box.contains(particleCell))) {
1053 MayDay::Error("ParticleContainer::remap -- error2: particle is not contained in grid box!");
1054 }
1055 }
1056 }
1057 }
1058}
1059
1060template <typename P>
1061inline void
1063{
1064 CH_TIME("ParticleContainer::transferParticlesToSingleList");
1065
1066 for (int lvl = 0; lvl < a_particles.size(); lvl++) {
1067 const BoxLayout& dbl = a_particles[lvl]->getBoxes();
1068 const DataIterator& dit = dbl.dataIterator();
1069
1070 const int nbox = dit.size();
1071
1072#pragma omp for schedule(runtime)
1073 for (int mybox = 0; mybox < nbox; mybox++) {
1074 const DataIndex& din = dit[mybox];
1075
1077
1078 a_list.catenate(particles);
1079 }
1080 }
1081}
1082
1083template <typename P>
1084inline void
1086{
1087 CH_TIME("ParticleContainer::copyParticlesToSingleList");
1088
1089 for (int lvl = 0; lvl < a_particles.size(); lvl++) {
1090 const BoxLayout& dbl = a_particles[lvl]->getBoxes();
1091 const DataIterator& dit = dbl.dataIterator();
1092
1093 const int nbox = dit.size();
1094
1095#pragma omp for schedule(runtime)
1096 for (int mybox = 0; mybox < nbox; mybox++) {
1097 const DataIndex& din = dit[mybox];
1098
1099 const List<P>& particles = (*a_particles[lvl])[din].listItems();
1100
1101 a_list.join(particles);
1102 }
1103 }
1104}
1105
1106template <typename P>
1107inline void
1109 List<P>& a_unmappedParticles) const noexcept
1110{
1111 CH_TIME("ParticleContainer::mapParticlesToAMRGrid");
1112
1113 const uint32_t numRanks = numProc();
1114 const uint32_t myRank = procID();
1115
1116 std::vector<RealVect> quasiDx(1 + m_finestLevel);
1117
1118 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
1119 quasiDx[lvl] = m_blockingFactor * m_dx[lvl];
1120 }
1121
1122 // Starting on the finest level -- figure out where the particles wound up. We are going from finest to coarsest here because we want
1123 // the particle to end up on the finest grid level that contains them.
1125 bool foundTile = false;
1126
1127 for (int lvl = m_finestLevel; lvl >= 0 && !foundTile; lvl--) {
1128 const IntVect particleTile = locateBin(lit().position(), quasiDx[lvl], m_probLo);
1129
1130 const auto& myLevelTiles = m_levelTiles[lvl]->getMyTiles();
1131 const auto& otherLevelTiles = m_levelTiles[lvl]->getOtherTiles();
1132
1133 if (myLevelTiles.find(particleTile) != myLevelTiles.end()) {
1134 // Found the particle on this level and this rank is the one who owns it.
1136 const uint32_t toRank = myRank;
1137
1139
1140 foundTile = true;
1141 }
1142#ifdef CH_MPI
1143 else if (otherLevelTiles.find(particleTile) != otherLevelTiles.end()) {
1144 // Particle was found on this level but no on this rank.
1146 const uint32_t& toRank = otherLevelTiles.at(particleTile).second;
1147
1149
1150 foundTile = true;
1151 }
1152#endif
1153 }
1154
1155 // If this triggers the particle fell off the domain and just move onto the next one.
1156 if (!foundTile) {
1157 ++lit;
1158 }
1159 }
1160
1161 // Rest of the particles fell of the domain and have to go.
1162 a_unmappedParticles.clear();
1163}
1164
1165template <typename P>
1166inline void
1169{
1170 CH_TIME("ParticleContainer::catenateParticleMaps");
1171
1172 const uint32_t numRanks = numProc();
1173
1175
1176 for (int irank = 0; irank < numRanks; irank++) {
1178
1179 for (auto& m : a_localParticles[irank]) {
1180 globalParticles[m.first].catenate(m.second);
1181 }
1182 }
1183}
1184
1185template <typename P>
1186inline void
1188 AMRParticles<P>& a_particleData) const noexcept
1189{
1190 CH_TIME("ParticleContainer::assignLocalParticles");
1191
1192#pragma omp parallel
1193 {
1194#pragma omp single
1195 {
1196 for (auto mapIter = a_mappedParticles.begin(); mapIter != a_mappedParticles.end(); mapIter++) {
1197#pragma omp task firstprivate(mapIter)
1198 {
1199 const uint32_t gridLevel = mapIter->first.first;
1200 const uint32_t gridIndex = mapIter->first.second;
1201 const DataIndex din = m_levelTiles[gridLevel]->getMyGrids().at(gridIndex);
1202
1203 List<P>& particles = mapIter->second;
1205
1206 myParticles.catenate(particles);
1207 }
1208 }
1209 }
1210 }
1211
1212 a_mappedParticles.clear();
1213}
1214
1215template <class P>
1216void
1218{
1219 CH_TIME("ParticleContainer::preRegrid");
1220 if (m_verbose) {
1221 pout() << "ParticleContainer::preRegrid" << endl;
1222 }
1223
1224 // TLDR: This routine takes all the particles on each level and puts them in a single list (per processor). After than
1225 // we can safely destruct the ParticleData on each level without losing the particles.
1226
1227 CH_assert(m_isDefined);
1228 if (m_isOrganizedByCell) {
1229 MayDay::Error("ParticleContainer::preRegrid - particles are sorted by cell!");
1230 }
1231
1232 // Fill cache particles on each level
1233 m_cacheParticles.resize(1 + m_finestLevel);
1234
1235 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
1236 m_cacheParticles[lvl] = RefCountedPtr<ParticleData<P>>(
1237 new ParticleData<P>(m_grids[lvl], m_domains[lvl], m_blockingFactor, m_dx[lvl], m_probLo));
1238
1239 const DisjointBoxLayout& dbl = m_grids[lvl];
1240 const DataIterator& dit = dbl.dataIterator();
1241
1242 const int nbox = dit.size();
1243
1244#pragma omp parallel for schedule(runtime)
1245 for (int mybox = 0; mybox < nbox; mybox++) {
1246 const DataIndex& din = dit[mybox];
1247
1248 List<P>& cacheParticles = (*m_cacheParticles[lvl])[din].listItems();
1250
1252 }
1253 }
1254}
1255
1256template <class P>
1257void
1259{
1260 CH_TIME("ParticleContainer::resetParticleIDs");
1261 if (m_verbose) {
1262 pout() << "ParticleContainer::resetParticleIDs" << endl;
1263 }
1264
1265 CH_assert(m_isDefined);
1266 if (m_isOrganizedByCell) {
1267 MayDay::Error("ParticleContainer::preRegrid - particles are sorted by cell!");
1268 }
1269
1270 int32_t counter = 0;
1271
1272 const auto myRank = static_cast<uint32_t>(procID());
1273
1274 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
1275 const DisjointBoxLayout& dbl = m_grids[lvl];
1276 const DataIterator& dit = dbl.dataIterator();
1277
1278 const int nbox = dit.size();
1279
1280#pragma omp parallel for schedule(runtime)
1281 for (int mybox = 0; mybox < nbox; mybox++) {
1282 const DataIndex& din = dit[mybox];
1283
1285
1286 for (ListIterator<P> lit(particles); lit.ok(); ++lit, counter++) {
1287 lit().rankID() = myRank;
1288 lit().particleID() = counter;
1289 }
1290 }
1291 }
1292}
1293
1294template <class P>
1295void
1298 const Vector<Real>& a_dx,
1299 const Vector<int>& a_refRat,
1302 const int a_lmin,
1303 const int a_newFinestLevel)
1304{
1305 CH_TIME("ParticleContainer::regrid");
1306 if (m_verbose) {
1307 pout() << "ParticleContainer::regrid" << endl;
1308 }
1309
1310 // TLDR: This is the main regrid routine for particle container. This is essentially a ::define() followed by several types of remapping steps
1311 CH_assert(m_isDefined);
1312
1313 if (m_isOrganizedByCell) {
1314 MayDay::Error("ParticleContainer::regrid(...) - particles are sorted by cell!");
1315 }
1316
1317 // Update this stuff
1318 m_grids = a_grids;
1319 m_domains = a_domains;
1320 m_refRat = a_refRat;
1321 m_validRegion = a_validMask;
1322 m_finestLevel = a_newFinestLevel;
1323 m_levelTiles = a_levelTiles;
1324
1325 m_dx.resize(1 + m_finestLevel);
1326 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
1327 m_dx[lvl] = a_dx[lvl] * RealVect::Unit;
1328 }
1329
1330 this->setupGrownGrids(a_lmin, m_finestLevel);
1331 this->setupParticleData(a_lmin, m_finestLevel);
1332
1333 // Perform the remapping operation.
1334 const uint32_t numRanks = numProc();
1335 const uint32_t myRank = procID();
1336
1338
1339 // These are the particles that get sent to each MPI rank. The pair contains the grid level and grid index.
1340 // Each MPI rank (and OpenMP thread) goes through it's particles and checks if they've fallen off the grid. If they have,
1341 // then the particles are moved onto appropriate lists that will get sent to each rank.
1343
1344#pragma omp parallel
1345 {
1347
1349
1350 // Collect particles owned by this rank/thread.
1351 this->transferParticlesToSingleList(outcasts, m_cacheParticles);
1352
1353 // Map the particles to other grid patches
1354 this->mapParticlesToAMRGrid(threadLocalParticlesToSend, outcasts);
1355
1356 // Catenate the thread-local particle lists onto particlesToSend
1357#pragma omp critical
1358 this->catenateParticleMaps(particlesToSend, threadLocalParticlesToSend);
1359 }
1360
1361 // Particles that go from this rank to this rank don't need to be communicated so that we can place them directly on the correct patch.
1362 this->assignLocalParticles(particlesToSend[myRank], m_particles);
1363
1364 // If using MPI then we have to scatter the particles across MPI ranks.
1365#ifdef CH_MPI
1367
1368 ParticleOps::scatterParticles(receivedParticles, particlesToSend);
1369
1370 // Assign particles to the correct level and grid patch -- we iterate through receivedParticles and decode the information
1371 // we got from there.
1372 this->assignLocalParticles(receivedParticles, m_particles);
1373#endif
1374
1375 if (m_debug) {
1376 this->sanityCheck();
1377 }
1378
1379 // Discard transient storage.
1380 m_cacheParticles.resize(0);
1381}
1382
1383template <class P>
1384template <Real& (P::*particleScalarField)()>
1385void
1387{
1388 CH_TIME("ParticleContainer::setValue");
1389
1390 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
1391 const DisjointBoxLayout& dbl = m_grids[lvl];
1392 const DataIterator& dit = dbl.dataIterator();
1393
1394 const int nbox = dit.size();
1395
1396#pragma omp parallel for schedule(runtime)
1397 for (int mybox = 0; mybox < nbox; mybox++) {
1398 const DataIndex& din = dit[mybox];
1399
1401
1402 for (ListIterator<P> lit(patchParticles); lit.ok(); ++lit) {
1403 P& p = lit();
1404
1406 }
1407 }
1408 }
1409}
1410
1411template <class P>
1412template <RealVect& (P::*particleVectorField)()>
1413void
1415{
1416 CH_TIME("ParticleContainer::setValue");
1417
1418 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
1419 const DisjointBoxLayout& dbl = m_grids[lvl];
1420 const DataIterator& dit = dbl.dataIterator();
1421
1422 const int nbox = dit.size();
1423
1424#pragma omp parallel for schedule(runtime)
1425 for (int mybox = 0; mybox < nbox; mybox++) {
1426 const DataIndex& din = dit[mybox];
1427
1429
1430 for (ListIterator<P> lit(patchParticles); lit.ok(); ++lit) {
1431 P& p = lit();
1432
1434 }
1435 }
1436 }
1437}
1438
1439template <class P>
1440unsigned long long
1442{
1443 CH_TIME("ParticleContainer::getNumberOfValidParticlesLocal");
1444
1445 CH_assert(m_isDefined);
1446
1447 unsigned long long n = 0;
1448
1449 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
1450 const DisjointBoxLayout& dbl = m_grids[lvl];
1451 const DataIterator& dit = dbl.dataIterator();
1452
1453 const int nbox = dit.size();
1454
1455#pragma omp parallel for schedule(runtime) reduction(+ : n)
1456 for (int mybox = 0; mybox < nbox; mybox++) {
1457 const DataIndex& din = dit[mybox];
1458
1459 const List<P>& particles = (*m_particles[lvl])[din].listItems();
1460
1461 n += (unsigned long long)particles.length();
1462 }
1463 }
1464
1465 return n;
1466}
1467
1468template <class P>
1469unsigned long long
1471{
1472 CH_TIME("ParticleContainer::getNumberOfValidParticlesGlobal");
1473
1474 CH_assert(m_isDefined);
1475
1476 const unsigned long long n = this->getNumberOfValidParticlesLocal();
1477
1478 return ParallelOps::sum(n);
1479}
1480
1481template <class P>
1482unsigned long long
1484{
1485 CH_TIME("ParticleContainer::getNumberOfOutcastParticlesLocal");
1486
1487 CH_assert(m_isDefined);
1488
1489 unsigned long long n = 0;
1490
1491 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
1492 const DisjointBoxLayout& dbl = m_grids[lvl];
1493 const DataIterator& dit = dbl.dataIterator();
1494
1495 const int nbox = dit.size();
1496
1497#pragma omp parallel for schedule(runtime) reduction(+ : n)
1498 for (int mybox = 0; mybox < nbox; mybox++) {
1499 const DataIndex& din = dit[mybox];
1500
1501 const List<P>& outcast = m_particles[lvl]->outcast();
1502
1503 n += (unsigned long long)outcast.length();
1504 }
1505 }
1506
1507 return n;
1508}
1509
1510template <class P>
1511unsigned long long
1513{
1514 CH_TIME("ParticleContainer::getNumberOfOutcastParticlesGlobal");
1515
1516 CH_assert(m_isDefined);
1517
1518 const unsigned long long n = this->getNumberOfOutcastParticlesLocal();
1519
1520 return ParallelOps::sum(n);
1521}
1522
1523template <class P>
1524unsigned long long
1526{
1527 CH_TIME("ParticleContainer::getNumberOfMaskParticlesLocal");
1528
1529 CH_assert(m_isDefined);
1530
1531 unsigned long long n = 0;
1532
1533 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
1534 const DisjointBoxLayout& dbl = m_grids[lvl];
1535 const DataIterator& dit = dbl.dataIterator();
1536
1537 const int nbox = dit.size();
1538
1539#pragma omp parallel for schedule(runtime) reduction(+ : n)
1540 for (int mybox = 0; mybox < nbox; mybox++) {
1541 const DataIndex& din = dit[mybox];
1542
1543 const List<P>& maskParticles = (*m_maskParticles[lvl])[din].listItems();
1544
1545 n += (unsigned long long)maskParticles.length();
1546 }
1547 }
1548
1549 return n;
1550}
1551
1552template <class P>
1553unsigned long long
1555{
1556 CH_TIME("ParticleContainer::getNumberOfMaskParticlesGlobal");
1557
1558 CH_assert(m_isDefined);
1559
1560 const unsigned long long n = this->getNumberOfMaskParticlesLocal();
1561
1562 return ParallelOps::sum(n);
1563}
1564
1565template <class P>
1566void
1568{
1569 CH_TIME("ParticleContainer::copyMaskParticles(amr)");
1570
1571 CH_assert(m_isDefined);
1572
1573 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
1574 if (!a_mask[lvl].isNull()) {
1575 this->copyMaskParticles(lvl, *a_mask[lvl]);
1576 }
1577 }
1578}
1579
1580template <class P>
1581void
1583{
1584 CH_TIME("ParticleContainer::copyMaskParticles(level)");
1585 if (m_verbose) {
1586 pout() << "ParticleContainer::copyMaskParticles(level)" << endl;
1587 }
1588
1589 CH_assert(m_isDefined);
1590
1591 m_maskParticles[a_level]->clear();
1592
1593 const RealVect dx = m_dx[a_level];
1594
1595 // Copy particles from m_particles to m_maskParticles if they lie in the input mask.
1596 const DisjointBoxLayout& dbl = m_grids[a_level];
1597 const DataIterator& dit = dbl.dataIterator();
1598
1599 const int nbox = dit.size();
1600
1601#pragma omp parallel for schedule(runtime)
1602 for (int mybox = 0; mybox < nbox; mybox++) {
1603 const DataIndex& din = dit[mybox];
1604
1605 const BaseFab<bool>& mask = a_mask[din];
1606
1607 if (mask.isUsable()) {
1608 const Box gridBox = m_grids[a_level][din];
1609 const Box maskBox = mask.box();
1610
1612
1613 List<P>& maskParticles = (*m_maskParticles[a_level])[din].listItems();
1615
1616 for (ListIterator<P> lit(particles); lit.ok(); ++lit) {
1617 const RealVect x = lit().position();
1618 const IntVect iv = ParticleOps::getParticleCellIndex(x, m_probLo, dx);
1619
1620 if (!(maskBox.contains(iv))) {
1621 std::cout << a_level << "\t" << iv << "\t" << x << "\t" << maskBox << endl;
1622
1623 MayDay::Error("ParticleContainer::copyMaskParticles -- logic bust. Particle has fallen off grid");
1624 }
1625 else if (mask(iv)) {
1626 maskParticles.add(lit());
1627 }
1628 }
1629 }
1630 }
1631}
1632
1633template <class P>
1634void
1636{
1637 CH_TIME("ParticleContainer::transferMaskParticles(amr)");
1638
1639 CH_assert(m_isDefined);
1640
1641 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
1642 if (!a_mask[lvl].isNull()) {
1643 this->transferMaskParticles(lvl, *a_mask[lvl]);
1644 }
1645 }
1646}
1647
1648template <class P>
1649void
1651{
1652 CH_TIME("ParticleContainer::transferMaskParticles(level)");
1653 if (m_verbose) {
1654 pout() << "ParticleContainer::transferMaskParticles(level)" << endl;
1655 }
1656
1657 CH_assert(m_isDefined);
1658
1659 const RealVect dx = m_dx[a_level];
1660
1661 // Copy particles from m_particles to m_maskParticles if they lie in the input mask.
1662 const DisjointBoxLayout& dbl = m_grids[a_level];
1663 const DataIterator& dit = dbl.dataIterator();
1664
1665 const int nbox = dit.size();
1666
1667#pragma omp parallel for schedule(runtime)
1668 for (int mybox = 0; mybox < nbox; mybox++) {
1669 const DataIndex& din = dit[mybox];
1670
1671 const BaseFab<bool>& mask = a_mask[din];
1672
1673 if (mask.isUsable()) {
1674 const Box gridBox = m_grids[a_level][din];
1675 const Box maskBox = mask.box();
1676
1678
1679 List<P>& maskParticles = (*m_maskParticles[a_level])[din].listItems();
1681
1682 for (ListIterator<P> lit(particles); lit.ok();) {
1683 const RealVect x = lit().position();
1684 const IntVect iv = ParticleOps::getParticleCellIndex(x, m_probLo, dx);
1685
1686 if (!(maskBox.contains(iv))) {
1687 // std::cout << a_level << "\t" << iv << "\t" << x << endl;
1688
1689 MayDay::Warning("ParticleContainer::transferMaskParticles -- logic bust. Particle has fallen off grid");
1690
1691 ++lit;
1692 }
1693 else {
1694 if (mask(iv)) {
1695 maskParticles.transfer(lit);
1696 }
1697 else {
1698 ++lit;
1699 }
1700 }
1701 }
1702 }
1703 }
1704}
1705
1706template <class P>
1707void
1709{
1710 CH_assert(m_isDefined);
1711
1712 this->clear(m_particles);
1713}
1714
1715template <class P>
1716void
1718{
1719 CH_assert(m_isDefined);
1720
1721 this->clear(m_bufferParticles);
1722}
1723
1724template <class P>
1725void
1727{
1728 CH_assert(m_isDefined);
1729
1730 this->clear(m_maskParticles);
1731}
1732
1733template <class P>
1734void
1736{
1737 CH_assert(m_isDefined);
1738
1739 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
1741
1742 List<P>& outcast = particleData.outcast();
1743
1744 outcast.clear();
1745 }
1746}
1747
1748template <class P>
1749void
1751{
1752 if (m_verbose) {
1753 pout() << "ParticleContainer::clear(AMRParticles)" << endl;
1754 }
1755
1756 CH_assert(m_isDefined);
1757
1758 for (int lvl = 0; lvl <= m_finestLevel; lvl++) {
1759
1761
1762 // Clear patch particles
1763 const BoxLayout& grids = levelParticles.getBoxes();
1764 const DataIterator& dit = grids.dataIterator();
1765
1766 const int nbox = dit.size();
1767
1768#pragma omp parallel for schedule(runtime)
1769 for (int mybox = 0; mybox < nbox; mybox++) {
1770 const DataIndex din = dit[mybox];
1771
1772 List<P>& patchParticles = levelParticles[din].listItems();
1773
1774 patchParticles.clear();
1775 }
1776
1777 // Clear outcast
1778 List<P>& outcast = levelParticles.outcast();
1779 outcast.clear();
1780 }
1781}
1782
1783#include <CD_NamespaceFooter.H>
1784
1785#endif
Declaration of a namespace for proto-typing grid and EB loops.
Declaration of various useful OpenMP-related utilities.
Agglomeration of basic MPI reductions.
Declaration of a class for holding particles on an AMR hierarchy.
std::unordered_map< std::pair< uint32_t, uint32_t >, T, PairHash > ParticleMap
Underlying particle map when gathering/scattering particles.
Definition CD_ParticleMap.H:75
Declaration of a static class containing some common useful particle routines that would otherwise be...
Implementation of CD_Timer.H.
Vector< RealVect > getDx() const noexcept
Get grid resolutions.
Definition CD_ParticleContainerImplem.H:257
RealVect getProbLo() const noexcept
Get lower-left corner.
Definition CD_ParticleContainerImplem.H:250
AMRCellParticles< P > & getCellParticles()
Get all cell particles.
Definition CD_ParticleContainerImplem.H:352
void organizeParticlesByPatch()
Sort particles by cell.
Definition CD_ParticleContainerImplem.H:502
void clearMaskParticles() const
Clear the "mask" particles.
Definition CD_ParticleContainerImplem.H:1726
unsigned long long getNumberOfOutcastParticlesGlobal() const
Get global number of particles.
Definition CD_ParticleContainerImplem.H:1512
unsigned long long getNumberOfValidParticlesLocal() const
Get local number of particles.
Definition CD_ParticleContainerImplem.H:1441
void getCellParticlesDestructive(BinFab< P > &a_cellParticles, int a_lvl, const DataIndex &a_dit)
Fill a cell-sorted particle data holder with all the particles in the grid patch. The original partic...
Definition CD_ParticleContainerImplem.H:422
void preRegrid(int a_lmin)
Cache particles before calling regrid.
Definition CD_ParticleContainerImplem.H:1217
ParticleData< P > & operator[](int a_lvl)
Get particle data on a level.
Definition CD_ParticleContainerImplem.H:326
std::string getRealm() const
Get the realm where this ParticleContainer lives.
Definition CD_ParticleContainerImplem.H:234
void regrid(const Vector< DisjointBoxLayout > &a_grids, const Vector< ProblemDomain > &a_domains, const Vector< Real > &a_dx, const Vector< int > &a_refRat, const Vector< ValidMask > &a_validMask, const Vector< RefCountedPtr< LevelTiles > > &a_levelTiles, int a_base, int a_newFinestLevel)
Regrid function. a_base is the coarsest grid level which did not change.
Definition CD_ParticleContainerImplem.H:1296
virtual ~ParticleContainer()
Destructor ( does nothing)
Definition CD_ParticleContainerImplem.H:64
void sortParticles() noexcept
Sort particles according to the < operator in the particle.
Definition CD_ParticleContainerImplem.H:191
const Vector< DisjointBoxLayout > & getGrids() const
Get the AMR grids.
Definition CD_ParticleContainerImplem.H:243
unsigned long long getNumberOfOutcastParticlesLocal() const
Get local number of particles.
Definition CD_ParticleContainerImplem.H:1483
void addParticles(const List< P > &a_particles)
Add particles to container.
Definition CD_ParticleContainerImplem.H:547
unsigned long long getNumberOfMaskParticlesGlobal() const
Get the number particles in the halo cells.
Definition CD_ParticleContainerImplem.H:1554
void define(const Vector< DisjointBoxLayout > &a_grids, const Vector< ProblemDomain > &a_domains, const Vector< Real > &a_dx, const Vector< int > &a_refRat, const Vector< ValidMask > &a_validMask, const Vector< RefCountedPtr< LevelTiles > > &a_levelTiles, const RealVect &a_probLo, int a_blockingFactor, int a_finestLevel, const std::string &a_realm)
Define the container. This will do a clear-out of all particles.
Definition CD_ParticleContainerImplem.H:71
void clear(AMRParticles< P > &a_particles) const
Clear particles on input data holder.
Definition CD_ParticleContainerImplem.H:1750
void remap()
Remap over the entire AMR hierarchy.
Definition CD_ParticleContainerImplem.H:971
void setupParticleData(int a_base, int a_finestLevel)
Setup function for the particle data (m_particles and m_maskParticles)
Definition CD_ParticleContainerImplem.H:148
void clearParticles()
Clear all particles.
Definition CD_ParticleContainerImplem.H:1708
void clearOutcast() noexcept
Clear outcast particles.
Definition CD_ParticleContainerImplem.H:1735
void catenateParticleMaps(std::vector< ParticleMap< List< P > > > &a_globalParticles, std::vector< ParticleMap< List< P > > > &a_localParticles) const noexcept
Catenate the particles. This is usually called within OpenMP parallel regions.
Definition CD_ParticleContainerImplem.H:1167
unsigned long long getNumberOfMaskParticlesLocal() const
Get the number particles in the halo cells.
Definition CD_ParticleContainerImplem.H:1525
void organizeParticlesByCell()
Sort particles by cell.
Definition CD_ParticleContainerImplem.H:468
void mapParticlesToAMRGrid(std::vector< ParticleMap< List< P > > > &a_mappedParticles, List< P > &a_unmappedParticles) const noexcept
Iterate through the unmapped particles and map them to proper level, grid indices,...
Definition CD_ParticleContainerImplem.H:1108
int getFinestLevel() const
Get finest AMR level.
Definition CD_ParticleContainerImplem.H:225
void clearBufferParticles() const
Clear the buffer particles.
Definition CD_ParticleContainerImplem.H:1717
void assignLocalParticles(ParticleMap< List< P > > &a_mappedParticles, AMRParticles< P > &a_particleData) const noexcept
Gather particles locally.
Definition CD_ParticleContainerImplem.H:1187
void transferParticlesToSingleList(List< P > &a_list, AMRParticles< P > &a_particles) const noexcept
Gather the particles onto a single list.
Definition CD_ParticleContainerImplem.H:1062
bool isOrganizedByCell() const
Is cell-sorted or not.
Definition CD_ParticleContainerImplem.H:218
void transferMaskParticles(const Vector< RefCountedPtr< LevelData< BaseFab< bool > > > > &a_mask)
Copy particles to the mask particle data holder.
Definition CD_ParticleContainerImplem.H:1635
void copyParticlesToSingleList(List< P > &a_list, const AMRParticles< P > &a_particles) const noexcept
Copy the input particles onto a single list.
Definition CD_ParticleContainerImplem.H:1085
ParticleContainer()
Default constructor. Leaves object in undefined state.
void sanityCheck() const noexcept
Run a sanity check and make sure all particles are in their correctly assigned box.
Definition CD_ParticleContainerImplem.H:1036
void setValue(Real a_value)
Set the particle member to the input value.
Definition CD_ParticleContainerImplem.H:1386
AMRParticles< P > & getMaskParticles()
Get the mask particles.
Definition CD_ParticleContainerImplem.H:308
unsigned long long getNumberOfValidParticlesGlobal() const
Get global number of particles.
Definition CD_ParticleContainerImplem.H:1470
void resetParticleIDs() noexcept
Compute new particle IDs.
Definition CD_ParticleContainerImplem.H:1258
void addParticlesDestructive(List< P > &a_particles)
Add particles to container. The input particles are destroyed by this routine.
Definition CD_ParticleContainerImplem.H:613
void setupGrownGrids(int a_base, int a_finestLevel)
Set up grown grids.
Definition CD_ParticleContainerImplem.H:117
AMRParticles< P > & getBufferParticles()
Get buffer particles on all levels.
Definition CD_ParticleContainerImplem.H:290
AMRParticles< P > & getParticles()
Get all particles on all levels.
Definition CD_ParticleContainerImplem.H:264
void transferParticles(ParticleContainer< P > &a_otherContainer)
Move particles into this container.
Definition CD_ParticleContainerImplem.H:912
void copyMaskParticles(const Vector< RefCountedPtr< LevelData< BaseFab< bool > > > > &a_mask) const
Copy particles to mask particle data holder.
Definition CD_ParticleContainerImplem.H:1567
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:31
Base class for a tracer particle solver. This solver can advance particles in a pre-defined velocity ...
Definition CD_TracerParticleSolver.H:38
ParticleContainer< P > m_particles
Computational particles.
Definition CD_TracerParticleSolver.H:412
TracerParticleSolver()
Default constructor.
Definition CD_TracerParticleSolverImplem.H:26
std::string m_realm
Realm where this solver lives.
Definition CD_TracerParticleSolver.H:352
virtual ParticleContainer< P > & getParticles()
Get all particles.
Definition CD_TracerParticleSolverImplem.H:663
Real sum(const Real &a_value) noexcept
Compute the sum across all MPI ranks.
Definition CD_ParallelOpsImplem.H:354