Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_NeighborParticlesI.H
Go to the documentation of this file.
1
2#include <iterator>
3
4namespace amrex {
5
6namespace detail {
7template <class>
8inline constexpr bool always_false_v = false;
9}
10
11template <typename T_ParticleType, int NArrayReal, int NArrayInt,
12 template<class> class Allocator, class CellAssignor>
14
15template <typename T_ParticleType, int NArrayReal, int NArrayInt,
16 template<class> class Allocator, class CellAssignor>
18
19template <typename T_ParticleType, int NArrayReal, int NArrayInt,
20 template<class> class Allocator, class CellAssignor>
21NeighborParticleContainer_impl<T_ParticleType, NArrayReal, NArrayInt, Allocator, CellAssignor>
22::NeighborParticleContainer_impl (ParGDBBase* gdb, int ncells)
24 m_num_neighbor_cells(ncells)
25{
27}
28
29template <typename T_ParticleType, int NArrayReal, int NArrayInt,
30 template<class> class Allocator, class CellAssignor>
33 const DistributionMapping & dmap,
34 const BoxArray & ba,
35 int nneighbor)
36 : ParticleContainerType(geom, dmap, ba),
37 m_num_neighbor_cells(nneighbor)
38{
40}
41
42template <typename T_ParticleType, int NArrayReal, int NArrayInt,
43 template<class> class Allocator, class CellAssignor>
46 const Vector<DistributionMapping> & dmap,
47 const Vector<BoxArray> & ba,
48 const Vector<int> & rr,
49 int nneighbor)
50 : ParticleContainerType(geom, dmap, ba, rr),
51 m_num_neighbor_cells(nneighbor)
52{
54}
55
56template <typename T_ParticleType, int NArrayReal, int NArrayInt,
57 template<class> class Allocator, class CellAssignor>
58void
61 for (int ii = 0; ii < numRealCommComps(); ++ii) {
62 ghost_real_comp.push_back(1);
63 }
64 for (int ii = 0; ii < numIntCommComps(); ++ii) {
65 ghost_int_comp.push_back(1);
66 }
67 calcCommSize();
68}
69
70template <typename T_ParticleType, int NArrayReal, int NArrayInt,
71 template<class> class Allocator, class CellAssignor>
72void
74::setRealCommComp (int i, bool value) {
75 ghost_real_comp[i] = value;
76 calcCommSize();
77}
78
79template <typename T_ParticleType, int NArrayReal, int NArrayInt,
80 template<class> class Allocator, class CellAssignor>
81void
83::setIntCommComp (int i, bool value) {
84 ghost_int_comp[i] = value;
85 calcCommSize();
86}
87
88template <typename T_ParticleType, int NArrayReal, int NArrayInt,
89 template<class> class Allocator, class CellAssignor>
90void
93 size_t comm_size = 0;
94 for (int ii = 0; ii < numRealCommComps(); ++ii) {
95 if (ghost_real_comp[ii]) {
96 comm_size += sizeof(ParticleReal);
97 }
98 }
99 for (int ii = 0; ii < numIntCommComps(); ++ii) {
100 if (ghost_int_comp[ii]) {
101 comm_size += sizeof(int);
102 }
103 }
104 if ( enableInverse() ) { comm_size += 4*sizeof(int); }
105 cdata_size = comm_size;
106}
107
108template <typename T_ParticleType, int NArrayReal, int NArrayInt,
109 template<class> class Allocator, class CellAssignor>
110void
112::Regrid (const DistributionMapping &dmap, const BoxArray &ba ) {
113 const int lev = 0;
114 AMREX_ASSERT(this->finestLevel() == 0);
115 this->SetParticleBoxArray(lev, ba);
116 this->SetParticleDistributionMap(lev, dmap);
117 this->Redistribute();
118}
119
120template <typename T_ParticleType, int NArrayReal, int NArrayInt,
121 template<class> class Allocator, class CellAssignor>
122void
124::Regrid (const DistributionMapping &dmap, const BoxArray &ba, int lev) {
125 AMREX_ASSERT(lev <= this->finestLevel());
126 this->SetParticleBoxArray(lev, ba);
127 this->SetParticleDistributionMap(lev, dmap);
128 this->Redistribute();
129}
130
131template <typename T_ParticleType, int NArrayReal, int NArrayInt,
132 template<class> class Allocator, class CellAssignor>
133void
136 AMREX_ASSERT(ba.size() == this->finestLevel()+1);
137 for (int lev = 0; lev < this->numLevels(); ++lev)
138 {
139 this->SetParticleBoxArray(lev, ba[lev]);
140 this->SetParticleDistributionMap(lev, dmap[lev]);
141 }
142 this->Redistribute();
143}
144
145template <typename T_ParticleType, int NArrayReal, int NArrayInt,
146 template<class> class Allocator, class CellAssignor>
147bool
150
151 BL_PROFILE("NeighborParticleContainer::areMasksValid");
152
153 resizeContainers(this->numLevels());
154
155 for (int lev = 0; lev < this->numLevels(); ++lev)
156 {
157 BoxArray ba = this->ParticleBoxArray(lev);
158 const DistributionMapping& dmap = this->ParticleDistributionMap(lev);
159
160 if (mask_ptr[lev] == nullptr ||
161 ! BoxArray::SameRefs(mask_ptr[lev]->boxArray(), ba) ||
162 ! DistributionMapping::SameRefs(mask_ptr[lev]->DistributionMap(), dmap))
163 {
164 return false;
165 }
166
167 // When a physical search radius is requested, also invalidate the mask
168 // if its existing grow is smaller than what the radius needs on this
169 // level. Avoids a silent undersizing when a caller previously built
170 // the mask with a smaller (e.g. zero) radius.
171 if (search_radius > amrex::Real(0)) {
172 const auto* dx_lev = this->Geom(lev).CellSize();
173 amrex::Real dx_min = dx_lev[0];
174 for (int d = 1; d < AMREX_SPACEDIM; ++d) {
175 dx_min = std::min(dx_min, dx_lev[d]);
176 }
177 int required = static_cast<int>(std::ceil(search_radius / dx_min));
178 if (mask_ptr[lev]->nGrow(0) < required) { return false; }
179 }
180 }
181 return true;
182}
183
184template <typename T_ParticleType, int NArrayReal, int NArrayInt,
185 template<class> class Allocator, class CellAssignor>
186void
188::BuildMasks (amrex::Real search_radius) {
189
190 BL_PROFILE("NeighborParticleContainer::BuildMasks");
191
192 if (this->numLevels() == 1) { use_mask = true; }
193 else { use_mask = false; }
194
195 resizeContainers(this->numLevels());
196
197 for (int lev = 0; lev < this->numLevels(); ++lev)
198 {
199 BoxArray ba = this->ParticleBoxArray(lev);
200 const DistributionMapping& dmap = this->ParticleDistributionMap(lev);
201
202 const Geometry& geom = this->Geom(lev);
203
204 // Mask grow: cell-based (m_num_neighbor_cells) by default. When a
205 // physical search radius is set, expand to ceil(radius / min(dx_lev))
206 // so the mask covers the requested radius along every axis (it uses a
207 // single scalar grow, so we size it by the smallest dx_lev).
208 int mask_ncells = m_num_neighbor_cells;
209 if (search_radius > amrex::Real(0)) {
210 const auto* dx_lev = geom.CellSize();
211 amrex::Real dx_min = dx_lev[0];
212 for (int d = 1; d < AMREX_SPACEDIM; ++d) {
213 dx_min = std::min(dx_min, dx_lev[d]);
214 }
215 mask_ncells = std::max(mask_ncells,
216 static_cast<int>(std::ceil(search_radius / dx_min)));
217 }
218
219 mask_ptr[lev] = std::make_unique<iMultiFab>(ba, dmap, int(num_mask_comps), mask_ncells);
220 mask_ptr[lev]->setVal(-1, mask_ncells);
221
222#ifdef AMREX_USE_OMP
223#pragma omp parallel
224#endif
225 for (MFIter mfi(*mask_ptr[lev],this->do_tiling ? this->tile_size : IntVect::TheZeroVector());
226 mfi.isValid(); ++mfi) {
227 const Box& box = mfi.tilebox();
228 const int grid_id = mfi.index();
229 const int tile_id = mfi.LocalTileIndex();
230 (*mask_ptr[lev])[mfi].template setVal<RunOn::Host>(grid_id, box, MaskComps::grid, 1);
231 (*mask_ptr[lev])[mfi].template setVal<RunOn::Host>(tile_id, box, MaskComps::tile, 1);
232 (*mask_ptr[lev])[mfi].template setVal<RunOn::Host>(lev , box, MaskComps::level, 1);
233 }
234
235 mask_ptr[lev]->FillBoundary(geom.periodicity());
236 }
237}
238
239template <typename T_ParticleType, int NArrayReal, int NArrayInt,
240 template<class> class Allocator, class CellAssignor>
241void
244{
245 BL_PROFILE("NeighborParticleContainer::GetNeighborCommTags");
246
247 local_neighbors.clear();
248 neighbor_procs.clear();
249
250 if (use_mask)
251 {
252 AMREX_ASSERT(this->finestLevel() == 0);
253 const int lev = 0;
254 for (MFIter mfi(*mask_ptr[lev],this->do_tiling ? this->tile_size : IntVect::TheZeroVector());
255 mfi.isValid(); ++mfi) {
256 const Box& box = mfi.growntilebox();
257 for (IntVect iv = box.smallEnd(); iv <= box.bigEnd(); box.next(iv)) {
258 const int grid = (*mask_ptr[lev])[mfi](iv, MaskComps::grid);
259 if (grid >= 0) {
260 const int tile = (*mask_ptr[lev])[mfi](iv, MaskComps::tile);
261 const int level = (*mask_ptr[lev])[mfi](iv, MaskComps::level);
262 const int global_proc = this->ParticleDistributionMap(level)[grid];
263 const int proc = ParallelContext::global_to_local_rank(global_proc);
264 NeighborCommTag comm_tag(proc, level, grid, tile);
265 local_neighbors.push_back(comm_tag);
266 if (proc != ParallelContext::MyProcSub()) {
267 neighbor_procs.push_back(proc);
268 }
269 }
270 }
271 }
272 }
273 else
274 {
275 for (int lev = 0; lev < this->numLevels(); ++lev)
276 {
277 for (MFIter mfi(*mask_ptr[lev],this->do_tiling ? this->tile_size : IntVect::TheZeroVector());
278 mfi.isValid(); ++mfi) {
279 const Box& box = mfi.validbox();
280 Vector<NeighborCommTag> comm_tags;
281 GetCommTagsBox(comm_tags, lev, box, search_radius);
282 for (auto const& tag : comm_tags) {
283 local_neighbors.push_back(tag);
284 if (tag.proc_id != ParallelContext::MyProcSub()) {
285 neighbor_procs.push_back(tag.proc_id);
286 }
287 }
288 }
289 }
290 }
291
292 RemoveDuplicates(local_neighbors);
293 RemoveDuplicates(neighbor_procs);
294}
295
296template <typename T_ParticleType, int NArrayReal, int NArrayInt,
297 template<class> class Allocator, class CellAssignor>
300::computeRefFac (int src_lev, int lev)
301{
302 IntVect ref_fac(1);
303 if (src_lev < lev) {
304 for (int l = src_lev; l < lev; ++l) {
305 ref_fac *= this->GetParGDB()->refRatio(l);
306 }
307 } else if (src_lev > lev) {
308 for (int l = src_lev; l > lev; --l) {
309 ref_fac *= this->GetParGDB()->refRatio(l-1);
310 }
311 ref_fac *= -1;
312 }
313 return ref_fac;
314}
315
316template <typename T_ParticleType, int NArrayReal, int NArrayInt,
317 template<class> class Allocator, class CellAssignor>
318void
320::GetCommTagsBox (Vector<NeighborCommTag>& tags, int src_lev, const Box& in_box,
321 amrex::Real search_radius)
322{
323 std::vector< std::pair<int, Box> > isects;
324 Box tbx;
325
326 for (int lev = 0; lev < this->numLevels(); ++lev) {
327 Box box = in_box;
328 const IntVect& ref_fac = computeRefFac(src_lev, lev);
329 if (ref_fac < IntVect::TheZeroVector())
330 {
331 box.coarsen(-1*ref_fac);
332 }
333 else if (ref_fac > IntVect::TheZeroVector())
334 {
335 box.refine(ref_fac);
336 }
337 if (search_radius > amrex::Real(0)) {
338 const auto* dx_lev = this->Geom(lev).CellSize();
339 IntVect grow_cells;
340 for (int d = 0; d < AMREX_SPACEDIM; ++d) {
341 grow_cells[d] = std::max(1, static_cast<int>(
342 std::ceil(search_radius / dx_lev[d])));
343 }
344 box.grow(grow_cells);
345 } else {
346 box.grow(computeRefFac(0, lev)*m_num_neighbor_cells);
347 }
348 const Periodicity& periodicity = this->Geom(lev).periodicity();
349 const std::vector<IntVect>& pshifts = periodicity.shiftIntVect();
350 const BoxArray& ba = this->ParticleBoxArray(lev);
351
352 for (auto const& pshift : pshifts)
353 {
354 const Box& pbox = box + pshift;
355 bool first_only = false;
356 ba.intersections(pbox, isects, first_only, 0);
357 for (const auto& isec : isects) {
358 const int grid = isec.first;
359 const int global_proc = this->ParticleDistributionMap(lev)[grid];
360 const int proc = ParallelContext::global_to_local_rank(global_proc);
361 for (IntVect iv = pbox.smallEnd(); iv <= pbox.bigEnd(); pbox.next(iv))
362 {
363 if (ba[grid].contains(iv))
364 {
365 int tile = getTileIndex(iv, ba[grid],
366 this->do_tiling, this->tile_size, tbx);
367 tags.push_back(NeighborCommTag(proc, lev, grid, tile));
368 }
369 }
370 }
371 }
372 }
373}
374
375template <typename T_ParticleType, int NArrayReal, int NArrayInt,
376 template<class> class Allocator, class CellAssignor>
377void
380
381 BL_PROFILE("NeighborParticleContainer::cacheNeighborInfo");
382
383 AMREX_ASSERT(this->OK());
384
385 resizeContainers(this->numLevels());
386
387 clearNeighbors();
388
389 AMREX_ASSERT(hasNeighbors() == false);
390
391 const int MyProc = ParallelContext::MyProcSub();
392
394 std::map<NeighborCommTag, Vector<NeighborIndexMap> > remote_map;
395
396 // tmp data structures used for OMP reduction
398 std::map<NeighborCommTag, Vector<Vector<NeighborIndexMap> > > tmp_remote_map;
399
400 local_map.resize(this->numLevels());
401 tmp_local_map.resize(this->numLevels());
402
403 int num_threads = OpenMP::get_max_threads();
404
405 for (int lev = 0; lev < this->numLevels(); ++lev) {
406 // resize our temporaries in serial
407 for (int i = 0; i < std::ssize(local_neighbors); ++i) {
408 const NeighborCommTag& comm_tag = local_neighbors[i];
409 tmp_remote_map[comm_tag].resize(num_threads);
410 remote_map[comm_tag];
411 PairIndex index(comm_tag.grid_id, comm_tag.tile_id);
412 tmp_local_map[lev][index].resize(num_threads);
413 local_map[lev][index];
414 buffer_tag_cache[lev][index].resize(num_threads);
415 }
416 }
417
418 for (int lev = 0; lev < this->numLevels(); ++lev) {
419 // First pass - each thread collects the NeighborIndexMaps it owes to other
420 // grids / tiles / procs
421#ifdef AMREX_USE_OMP
422#pragma omp parallel
423#endif
424 {
426 tags.reserve(AMREX_D_TERM(3, *3, *3));
427 for (MyParIter pti(*this, lev); pti.isValid(); ++pti) {
428 int thread_num = OpenMP::get_thread_num();
429 const int& grid = pti.index();
430 const int& tile = pti.LocalTileIndex();
431 PairIndex src_index(grid, tile);
432
433 NeighborCopyTag src_tag(lev, grid, tile);
434
435 auto& cache = buffer_tag_cache[lev][src_index][thread_num];
436
437 auto const ptd = pti.GetParticleTile().getConstParticleTileData();
438 for (int i = 0; i < pti.numParticles(); ++i) {
439 getNeighborTags(tags, ptd[i], m_num_neighbor_cells, src_tag, pti, search_radius);
440
441 // Add neighbors to buffers
442 for (int j = 0; j < std::ssize(tags); ++j) {
443 NeighborCopyTag& tag = tags[j];
444 PairIndex dst_index(tag.grid, tag.tile);
445 if (tag.grid < 0) { continue; }
446
447 tag.src_index = i;
448 const int cache_index = cache.size();
449 cache.push_back(tag);
450
451 const int global_who = this->ParticleDistributionMap(tag.level)[tag.grid];
452 const int who = ParallelContext::global_to_local_rank(global_who);
453 NeighborIndexMap nim(tag.level, dst_index.first, dst_index.second, -1,
454 lev, src_index.first, src_index.second,
455 cache_index, thread_num);
456 if (who == MyProc) {
457 auto& tmp = tmp_local_map[tag.level][dst_index];
458 Vector<NeighborIndexMap>& buffer = tmp[thread_num];
459 buffer.push_back(nim);
460 } else {
461 NeighborCommTag comm_tag(who, tag.level, tag.grid, tag.tile);
462 Vector<NeighborIndexMap>& buffer = tmp_remote_map[comm_tag][thread_num];
463 buffer.push_back(nim);
464 }
465 }
466 tags.clear();
467 }
468 }
469 }
470 }
471
472 for (int lev = 0; lev < this->numLevels(); ++lev) {
473 // second pass - for each tile, collect the neighbors owed from all threads
474#ifdef AMREX_USE_OMP
475#pragma omp parallel
476#endif
477 for (MFIter mfi = this->MakeMFIter(lev); mfi.isValid(); ++mfi) {
478 const int grid = mfi.index();
479 const int tile = mfi.LocalTileIndex();
480 PairIndex index(grid, tile);
481 for (int i = 0; i < num_threads; ++i) {
482 local_map[lev][index].insert(local_map[lev][index].end(),
483 tmp_local_map[lev][index][i].begin(),
484 tmp_local_map[lev][index][i].end());
485 tmp_local_map[lev][index][i].erase(tmp_local_map[lev][index][i].begin(),
486 tmp_local_map[lev][index][i].end());
487 }
488 }
489 }
490
491 // do the same for the remote neighbors
492 typename std::map<NeighborCommTag, Vector<Vector<NeighborIndexMap> > >::iterator it;
493#ifdef AMREX_USE_OMP
494#pragma omp parallel
495#pragma omp single nowait
496#endif
497 for (it=tmp_remote_map.begin(); it != tmp_remote_map.end(); it++) {
498#ifdef AMREX_USE_OMP
499#pragma omp task firstprivate(it)
500#endif
501 {
502 const NeighborCommTag& tag = it->first;
503 Vector<Vector<NeighborIndexMap> >& tmp = it->second;
504 for (int i = 0; i < num_threads; ++i) {
505 remote_map[tag].insert(remote_map[tag].end(), tmp[i].begin(), tmp[i].end());
506 tmp[i].erase(tmp[i].begin(), tmp[i].end());
507 }
508 }
509 }
510
511 for (int lev = 0; lev < this->numLevels(); ++lev) {
512 // now for the local neighbors, allocate buffers and cache
513 for (MFIter mfi = this->MakeMFIter(lev); mfi.isValid(); ++mfi) {
514 const int grid = mfi.index();
515 const int tile = mfi.LocalTileIndex();
516 PairIndex dst_index(grid, tile);
517 const Vector<NeighborIndexMap>& map = local_map[lev][dst_index];
518 const int num_ghosts = map.size();
519 neighbors[lev][dst_index].define(this->NumRuntimeRealComps(),
520 this->NumRuntimeIntComps(),
521 nullptr, nullptr, this->arena());
522 neighbors[lev][dst_index].resize(num_ghosts);
523 local_neighbor_sizes[lev][dst_index] = neighbors[lev][dst_index].size();
524 }
525 }
526
527 for (int lev = 0; lev < this->numLevels(); ++lev) {
528 for (MFIter mfi = this->MakeMFIter(lev); mfi.isValid(); ++mfi) {
529 const int grid = mfi.index();
530 const int tile = mfi.LocalTileIndex();
531 PairIndex dst_index(grid, tile);
532 const Vector<NeighborIndexMap>& map = local_map[lev][dst_index];
533 const int num_ghosts = map.size();
534#ifdef AMREX_USE_OMP
535#pragma omp parallel for
536#endif
537 for (int i = 0; i < num_ghosts; ++i) {
538 const NeighborIndexMap& nim = map[i];
539 PairIndex src_index(nim.src_grid, nim.src_tile);
540 Vector<NeighborCopyTag>& tags = buffer_tag_cache[nim.src_level][src_index][nim.thread_num];
541 AMREX_ASSERT(nim.src_index < tags.size());
542 tags[nim.src_index].dst_index = i;
543 AMREX_ASSERT(size_t(tags[nim.src_index].dst_index) < neighbors[nim.dst_level][dst_index].size());
544 }
545 }
546 }
547
548 // now we allocate the send buffers and cache the remotes
549 std::map<int, int> tile_counts;
550 for (const auto& kv: remote_map) {
551 tile_counts[kv.first.proc_id] += 1;
552 }
553
554 for (const auto& kv: remote_map) {
555 if (kv.first.proc_id == MyProc) { continue; }
556 Vector<char>& buffer = send_data[kv.first.proc_id];
557 buffer.resize(sizeof(int));
558 std::memcpy(buffer.data(), &tile_counts[kv.first.proc_id], sizeof(int));
559 }
560
561 for (auto& kv : remote_map) {
562 if (kv.first.proc_id == MyProc) { continue; }
563 int np = kv.second.size();
564 int data_size = np * cdata_size;
565 Vector<char>& buffer = send_data[kv.first.proc_id];
566 size_t old_size = buffer.size();
567 size_t new_size = buffer.size() + 4*sizeof(int) + data_size;
568 buffer.resize(new_size);
569 char* dst = &buffer[old_size];
570 std::memcpy(dst, &(kv.first.level_id), sizeof(int)); dst += sizeof(int);
571 std::memcpy(dst, &(kv.first.grid_id ), sizeof(int)); dst += sizeof(int);
572 std::memcpy(dst, &(kv.first.tile_id ), sizeof(int)); dst += sizeof(int);
573 std::memcpy(dst, &data_size, sizeof(int)); dst += sizeof(int);
574 size_t buffer_offset = old_size + 4*sizeof(int);
575#ifdef AMREX_USE_OMP
576#pragma omp parallel for
577#endif
578 for (int i = 0; i < np; ++i) {
579 const NeighborIndexMap& nim = kv.second[i];
580 PairIndex src_index(nim.src_grid, nim.src_tile);
581 Vector<NeighborCopyTag>& tags = buffer_tag_cache[nim.src_level][src_index][nim.thread_num];
582 tags[nim.src_index].dst_index = buffer_offset + i*cdata_size;
583 }
584 }
585
586 if ( enableInverse() )
587 {
588 for (int lev = 0; lev < this->numLevels(); ++lev)
589 {
590 for (const auto& kv : neighbors[lev])
591 {
592 inverse_tags[lev][kv.first].resize(kv.second.size());
593 }
594 }
595 }
596}
597
598template <typename T_ParticleType, int NArrayReal, int NArrayInt,
599 template<class> class Allocator, class CellAssignor>
600template <typename P>
601void
604 int nGrow, const NeighborCopyTag& src_tag, const MyParIter& pti,
605 amrex::Real search_radius)
606{
607 getNeighborTags(tags, p, IntVect(AMREX_D_DECL(nGrow, nGrow, nGrow)), src_tag, pti, search_radius);
608}
609
610template <typename T_ParticleType, int NArrayReal, int NArrayInt,
611 template<class> class Allocator, class CellAssignor>
612template <typename P>
613void
616 const IntVect& nGrow, const NeighborCopyTag& src_tag, const MyParIter& pti,
617 amrex::Real search_radius)
618{
619 Box shrink_box = pti.tilebox();
620 shrink_box.grow(-nGrow);
621
622 // Position-based early exit: when search_radius is set, skip particles
623 // that are farther than search_radius from all tile faces.
624 if (search_radius > amrex::Real(0)) {
625 const Box& tile_box = pti.tilebox();
626 const int src_lev = src_tag.level;
627 const auto* dx = this->Geom(src_lev).CellSize();
628 const auto* plo = this->Geom(src_lev).ProbLo();
629 bool is_interior = true;
630 for (int d = 0; d < AMREX_SPACEDIM; ++d) {
631 amrex::Real face_lo = plo[d] + tile_box.smallEnd(d) * dx[d];
632 amrex::Real face_hi = plo[d] + (tile_box.bigEnd(d) + 1) * dx[d];
633 if ((p.pos(d) - face_lo) < search_radius ||
634 (face_hi - p.pos(d)) < search_radius) {
635 is_interior = false;
636 break;
637 }
638 }
639 if (is_interior) { return; }
640 }
641
642 if (use_mask) {
643 const BaseFab<int>& mask = (*mask_ptr[src_tag.level])[src_tag.grid];
644 AMREX_ASSERT(this->finestLevel() == 0);
645 AMREX_ASSERT(src_tag.level == 0);
646
647 const int lev = 0;
648 const IntVect& iv = this->Index(p, lev);
649 if (shrink_box.contains(iv)) { return; }
650
651 const Periodicity& periodicity = this->Geom(lev).periodicity();
652 const Box& domain = this->Geom(lev).Domain();
653 const IntVect& lo = domain.smallEnd();
654 const IntVect& hi = domain.bigEnd();
655
656 // Figure out all our neighbors, removing duplicates
658 for (int ii = -nGrow[0]; ii < nGrow[0] + 1; ii += nGrow[0]) {,
659 for (int jj = -nGrow[1]; jj < nGrow[1] + 1; jj += nGrow[1]) {,
660 for (int kk = -nGrow[2]; kk < nGrow[2] + 1; kk += nGrow[2]) {)
661 if (AMREX_D_TERM((ii == 0), && (jj == 0), && (kk == 0))) { continue; }
662 IntVect shift(AMREX_D_DECL(ii, jj, kk));
663 IntVect neighbor_cell = iv + shift;
664
665 NeighborCopyTag tag;
666 tag.grid = mask(neighbor_cell, MaskComps::grid);
667 tag.tile = mask(neighbor_cell, MaskComps::tile);
668 tag.level = mask(neighbor_cell, MaskComps::level);
669 if (periodicity.isAnyPeriodic()) {
670 for (int dir = 0; dir < AMREX_SPACEDIM; ++dir) {
671 if (! periodicity.isPeriodic(dir)) { continue; }
672 if (neighbor_cell[dir] < lo[dir]) {
673 tag.periodic_shift[dir] = -1;
674 } else if (neighbor_cell[dir] > hi[dir]) {
675 tag.periodic_shift[dir] = 1;
676 }
677 }
678 }
679
680 if (tag != src_tag) { tags.push_back(tag); }
681
683 },
684 },
685 })
686
687 RemoveDuplicates(tags);
688 return;
689 }
690 else
691 {
692 std::vector< std::pair<int, Box> > isects;
693 Box tbx;
694 for (int lev = 0; lev < this->numLevels(); ++lev)
695 {
696 IntVect ref_fac = computeRefFac(0, lev);
697 const Periodicity& periodicity = this->Geom(lev).periodicity();
698 const std::vector<IntVect>& pshifts = periodicity.shiftIntVect();
699 const BoxArray& ba = this->ParticleBoxArray(lev);
700 const IntVect& iv = this->Index(p, lev);
701 for (auto const& pshift : pshifts)
702 {
703 Box pbox;
704 if (search_radius > amrex::Real(0)) {
705 const auto* dx_lev = this->Geom(lev).CellSize();
706 IntVect grow_cells;
707 for (int d = 0; d < AMREX_SPACEDIM; ++d) {
708 grow_cells[d] = std::max(1, static_cast<int>(
709 std::ceil(search_radius / dx_lev[d])));
710 }
711 pbox = amrex::grow(Box(iv, iv), grow_cells) + pshift;
712 } else {
713 pbox = amrex::grow(Box(iv, iv), ref_fac*nGrow) + pshift;
714 }
715 bool first_only = false;
716 ba.intersections(pbox, isects, first_only, 0);
717 for (const auto& isec : isects)
718 {
719 const Box& grid_box = ba[isec.first];
720 for (IntVect cell = pbox.smallEnd(); cell <= pbox.bigEnd(); pbox.next(cell)) {
721 if ( !grid_box.contains(cell) ) { continue; }
722 int tile = getTileIndex(cell, grid_box,
723 this->do_tiling, this->tile_size, tbx);
724 auto nbor = NeighborCopyTag(lev, isec.first, tile);
725 nbor.periodic_shift = -pshift;
726 if (src_tag != nbor) { tags.push_back(nbor); }
727 }
728 }
729 }
730 }
731
732 RemoveDuplicates(tags);
733 return;
734 }
735}
736
737template <typename T_ParticleType, int NArrayReal, int NArrayInt,
738 template<class> class Allocator, class CellAssignor>
739void
742#ifdef AMREX_USE_GPU
743 fillNeighborsGPU();
744#else
745 fillNeighborsCPU();
746#endif
747 m_has_neighbors = true;
748}
749
750template <typename T_ParticleType, int NArrayReal, int NArrayInt,
751 template<class> class Allocator, class CellAssignor>
752void
755 // CPU-only path: GPU build of the radius-based neighbor search is not
756 // yet implemented.
757#ifdef AMREX_USE_GPU
758 amrex::ignore_unused(search_radius);
760 "fillNeighbors(search_radius) is not implemented for GPU builds.");
761#else
762 fillNeighborsCPU(search_radius);
763#endif
764 m_has_neighbors = true;
765}
766
767template <typename T_ParticleType, int NArrayReal, int NArrayInt,
768 template<class> class Allocator, class CellAssignor>
769void
771::sumNeighbors (int real_start_comp, int real_num_comp,
772 int int_start_comp, int int_num_comp) {
773#ifdef AMREX_USE_GPU
774 amrex::ignore_unused(real_start_comp,real_num_comp,int_start_comp,int_num_comp);
775 amrex::Abort("NeighborParticleContainer::sumNeighbors is not implemented for GPU builds");
776#else
777 sumNeighborsCPU(real_start_comp, real_num_comp, int_start_comp, int_num_comp);
778#endif
779}
780
781template <typename T_ParticleType, int NArrayReal, int NArrayInt,
782 template<class> class Allocator, class CellAssignor>
783void
785::updateNeighbors (bool boundary_neighbors_only)
786{
787
788 AMREX_ASSERT(hasNeighbors());
789
790#ifdef AMREX_USE_GPU
791 updateNeighborsGPU(boundary_neighbors_only);
792#else
793 amrex::ignore_unused(boundary_neighbors_only);
794 updateNeighborsCPU(true);
795#endif
796 m_has_neighbors = true;
797}
798
799template <typename T_ParticleType, int NArrayReal, int NArrayInt,
800 template<class> class Allocator, class CellAssignor>
801void
804{
805#ifdef AMREX_USE_GPU
806 clearNeighborsGPU();
807#else
808 clearNeighborsCPU();
809#endif
810 m_has_neighbors = false;
811}
812
813template <typename T_ParticleType, int NArrayReal, int NArrayInt,
814 template<class> class Allocator, class CellAssignor>
815template <class CheckPair>
816void
818buildNeighborList (CheckPair const& check_pair, bool /*sort*/)
819{
820 AMREX_ASSERT(numParticlesOutOfRange(*this, m_num_neighbor_cells) == 0);
821
822 BL_PROFILE("NeighborParticleContainer::buildNeighborList");
823
824 resizeContainers(this->numLevels());
825
826 for (int lev = 0; lev < this->numLevels(); ++lev)
827 {
828 m_neighbor_list[lev].clear();
829
830 for (MyParIter pti(*this, lev); pti.isValid(); ++pti) {
831 PairIndex index(pti.index(), pti.LocalTileIndex());
832 m_neighbor_list[lev][index];
833 }
834
835#ifndef AMREX_USE_GPU
836 neighbor_list[lev].clear();
837 for (MyParIter pti(*this, lev); pti.isValid(); ++pti) {
838 PairIndex index(pti.index(), pti.LocalTileIndex());
839 neighbor_list[lev][index];
840 }
841#endif
842
843 auto& plev = this->GetParticles(lev);
844 const auto& geom = this->Geom(lev);
845
846#ifdef AMREX_USE_OMP
847#pragma omp parallel if (Gpu::notInLaunchRegion())
848#endif
849 for (MyParIter pti(*this, lev); pti.isValid(); ++pti)
850 {
851 int gid = pti.index();
852 int tid = pti.LocalTileIndex();
853 auto index = std::make_pair(gid, tid);
854
855 auto& ptile = plev[index];
856
857 if (ptile.numParticles() == 0) { continue; }
858
859 Box bx = pti.tilebox();
860 int ng = computeRefFac(0, lev).max()*m_num_neighbor_cells;
861 bx.grow(ng);
862
863 Gpu::DeviceVector<int> off_bins_v;
868
869 off_bins_v.push_back(0);
870 off_bins_v.push_back(int(bx.numPts()));
871 lo_v.push_back(lbound(bx));
872 hi_v.push_back(ubound(bx));
873 dxi_v.push_back(geom.InvCellSizeArray());
874 plo_v.push_back(geom.ProbLoArray());
875
876 m_neighbor_list[lev][index].build(ptile,
877 check_pair,
878 off_bins_v, dxi_v, plo_v, lo_v, hi_v, ng);
879
880#ifndef AMREX_USE_GPU
881 const auto& counts = m_neighbor_list[lev][index].GetCounts();
882 const auto& list = m_neighbor_list[lev][index].GetList();
883
884 int li = 0;
885 for (int i = 0; i < ptile.numParticles(); ++i)
886 {
887 auto cnt = counts[i];
888 neighbor_list[lev][index].push_back(cnt);
889 for (size_t j = 0; j < cnt; ++j)
890 {
891 neighbor_list[lev][index].push_back(list[li++]+1);
892 }
893 }
894#endif
895 }
896 }
897}
898
899template <typename T_ParticleType, int NArrayReal, int NArrayInt,
900 template<class> class Allocator, class CellAssignor>
901template <class CheckPair>
902void
904buildNeighborList (CheckPair const& check_pair, amrex::Real bin_size, bool /*sort*/)
905{
906 AMREX_ASSERT(numParticlesOutOfRange(*this, m_num_neighbor_cells) == 0);
907
908 BL_PROFILE("NeighborParticleContainer::buildNeighborList(bin_size)");
909
910 resizeContainers(this->numLevels());
911
912 for (int lev = 0; lev < this->numLevels(); ++lev)
913 {
914 m_neighbor_list[lev].clear();
915
916 for (MyParIter pti(*this, lev); pti.isValid(); ++pti) {
917 PairIndex index(pti.index(), pti.LocalTileIndex());
918 m_neighbor_list[lev][index];
919 }
920
921#ifndef AMREX_USE_GPU
922 neighbor_list[lev].clear();
923 for (MyParIter pti(*this, lev); pti.isValid(); ++pti) {
924 PairIndex index(pti.index(), pti.LocalTileIndex());
925 neighbor_list[lev][index];
926 }
927#endif
928
929 auto& plev = this->GetParticles(lev);
930 const auto& geom = this->Geom(lev);
931 const auto* dx_arr = geom.CellSize();
932 const auto* plo_arr = geom.ProbLo();
933
934#ifdef AMREX_USE_OMP
935#pragma omp parallel if (Gpu::notInLaunchRegion())
936#endif
937 for (MyParIter pti(*this, lev); pti.isValid(); ++pti)
938 {
939 int gid = pti.index();
940 int tid = pti.LocalTileIndex();
941 auto index = std::make_pair(gid, tid);
942
943 auto& ptile = plev[index];
944
945 if (ptile.numParticles() == 0) { continue; }
946
947 Box tile_box = pti.tilebox();
948 int ng = computeRefFac(0, lev).max() * m_num_neighbor_cells;
949
950 // Build fine bins at bin_size resolution, independent of mesh dx.
951 // The tile box (grown by ng cells) gives the physical extent the
952 // bins must cover; nbins is then ceil(extent / bin_size) per axis.
953 int nbins[AMREX_SPACEDIM];
954 amrex::Real phys_lo[AMREX_SPACEDIM], phys_hi[AMREX_SPACEDIM];
955 for (int d = 0; d < AMREX_SPACEDIM; ++d) {
956 phys_lo[d] = plo_arr[d] + (tile_box.smallEnd(d) - ng) * dx_arr[d];
957 phys_hi[d] = plo_arr[d] + (tile_box.bigEnd(d) + 1 + ng) * dx_arr[d];
958 nbins[d] = std::max(1, static_cast<int>(
959 std::ceil(static_cast<amrex::Real>(phys_hi[d] - phys_lo[d]) / bin_size)));
960 }
961
964 for (int d = 0; d < AMREX_SPACEDIM; ++d) {
965 fine_dxi[d] = static_cast<Real>(nbins[d]) / (phys_hi[d] - phys_lo[d]);
966 fine_plo[d] = phys_lo[d];
967 }
968
969 Dim3 fine_lo{.x=0, .y=0, .z=0};
970 Dim3 fine_hi{.x=nbins[0]-1,
971 .y=AMREX_D_PICK(0, nbins[1]-1, nbins[1]-1),
972 .z=AMREX_D_PICK(0, 0, nbins[2]-1)};
973
974 Gpu::DeviceVector<int> off_bins_v;
979
980 off_bins_v.push_back(0);
981 off_bins_v.push_back(AMREX_D_TERM(nbins[0], * nbins[1], * nbins[2]));
982 lo_v.push_back(fine_lo);
983 hi_v.push_back(fine_hi);
984 dxi_v.push_back(fine_dxi);
985 plo_v.push_back(fine_plo);
986
987 // num_cells=1: search +/-1 bin in each direction. Caller must
988 // choose bin_size >= interaction radius for completeness.
989 m_neighbor_list[lev][index].build(ptile,
990 check_pair,
991 off_bins_v, dxi_v, plo_v, lo_v, hi_v, 1);
992
993#ifndef AMREX_USE_GPU
994 const auto& counts = m_neighbor_list[lev][index].GetCounts();
995 const auto& list = m_neighbor_list[lev][index].GetList();
996
997 int li = 0;
998 for (int i = 0; i < ptile.numParticles(); ++i)
999 {
1000 auto cnt = counts[i];
1001 neighbor_list[lev][index].push_back(cnt);
1002 for (size_t j = 0; j < cnt; ++j)
1003 {
1004 neighbor_list[lev][index].push_back(list[li++]+1);
1005 }
1006 }
1007#endif
1008 }
1009 }
1010}
1011
1012template <typename T_ParticleType, int NArrayReal, int NArrayInt,
1013 template<class> class Allocator, class CellAssignor>
1014template <class CheckPair, class OtherPCType>
1015void
1017buildNeighborList (CheckPair const& check_pair, OtherPCType& other,
1018 Vector<std::map<std::pair<int, int>,
1020 bool /*sort*/)
1021{
1022 BL_PROFILE("NeighborParticleContainer::buildNeighborList");
1023
1024 AMREX_ASSERT(numParticlesOutOfRange(*this, m_num_neighbor_cells) == 0);
1025 AMREX_ASSERT(numParticlesOutOfRange(other, m_num_neighbor_cells) == 0);
1026 AMREX_ASSERT(SameIteratorsOK(*this, other));
1027
1028 EnsureThreadSafeTiles(*this);
1029 EnsureThreadSafeTiles(other);
1030
1031 resizeContainers(this->numLevels());
1032 neighbor_lists.resize(this->numLevels());
1033
1034 for (int lev = 0; lev < this->numLevels(); ++lev)
1035 {
1036 neighbor_lists[lev].clear();
1037
1038 for (MyParIter pti(*this, lev); pti.isValid(); ++pti) {
1039 PairIndex index(pti.index(), pti.LocalTileIndex());
1040 neighbor_lists[lev][index];
1041 }
1042
1043 auto& plev = this->GetParticles(lev);
1044 const auto& geom = this->Geom(lev);
1045
1046#ifdef AMREX_USE_OMP
1047#pragma omp parallel if (Gpu::notInLaunchRegion())
1048#endif
1049 for (MyParIter pti(*this, lev); pti.isValid(); ++pti)
1050 {
1051 int gid = pti.index();
1052 int tid = pti.LocalTileIndex();
1053 auto index = std::make_pair(gid, tid);
1054
1055 const auto& ptile = plev[index];
1056 auto& other_ptile = other.ParticlesAt(lev, pti);
1057 if (ptile.numParticles() == 0) { continue; }
1058
1059 Box bx = pti.tilebox();
1060 int ng = computeRefFac(0, lev).max()*m_num_neighbor_cells;
1061 bx.grow(ng);
1062
1063 Gpu::DeviceVector<int> off_bins_v;
1068
1069 off_bins_v.push_back(0);
1070 off_bins_v.push_back(int(bx.numPts()));
1071 lo_v.push_back(lbound(bx));
1072 hi_v.push_back(ubound(bx));
1073 dxi_v.push_back(geom.InvCellSizeArray());
1074 plo_v.push_back(geom.ProbLoArray());
1075
1076 neighbor_lists[lev][index].build(ptile, other_ptile,
1077 check_pair,
1078 off_bins_v, dxi_v, plo_v, lo_v, hi_v, ng);
1079 }
1080 }
1081}
1082
1083template <typename T_ParticleType, int NArrayReal, int NArrayInt,
1084 template<class> class Allocator, class CellAssignor>
1085template <class CheckPair, class OtherPCType>
1086void
1088buildNeighborList (CheckPair const& check_pair, OtherPCType& other,
1089 Vector<std::map<std::pair<int, int>,
1091 amrex::Real bin_size, bool /*sort*/)
1092{
1093 BL_PROFILE("NeighborParticleContainer::buildNeighborList(bin_size,other)");
1094
1095 AMREX_ASSERT(numParticlesOutOfRange(*this, m_num_neighbor_cells) == 0);
1096 AMREX_ASSERT(numParticlesOutOfRange(other, m_num_neighbor_cells) == 0);
1097 AMREX_ASSERT(SameIteratorsOK(*this, other));
1098
1099 EnsureThreadSafeTiles(*this);
1100 EnsureThreadSafeTiles(other);
1101
1102 resizeContainers(this->numLevels());
1103 neighbor_lists.resize(this->numLevels());
1104
1105 for (int lev = 0; lev < this->numLevels(); ++lev)
1106 {
1107 neighbor_lists[lev].clear();
1108
1109 for (MyParIter pti(*this, lev); pti.isValid(); ++pti) {
1110 PairIndex index(pti.index(), pti.LocalTileIndex());
1111 neighbor_lists[lev][index];
1112 }
1113
1114 auto& plev = this->GetParticles(lev);
1115 const auto& geom = this->Geom(lev);
1116 const auto* dx_arr = geom.CellSize();
1117 const auto* plo_arr = geom.ProbLo();
1118
1119#ifdef AMREX_USE_OMP
1120#pragma omp parallel if (Gpu::notInLaunchRegion())
1121#endif
1122 for (MyParIter pti(*this, lev); pti.isValid(); ++pti)
1123 {
1124 int gid = pti.index();
1125 int tid = pti.LocalTileIndex();
1126 auto index = std::make_pair(gid, tid);
1127
1128 const auto& ptile = plev[index];
1129 auto& other_ptile = other.ParticlesAt(lev, pti);
1130 if (ptile.numParticles() == 0) { continue; }
1131
1132 Box tile_box = pti.tilebox();
1133 int ng = computeRefFac(0, lev).max() * m_num_neighbor_cells;
1134
1135 // Build fine bins at bin_size resolution, independent of mesh dx.
1136 int nbins[AMREX_SPACEDIM];
1137 amrex::Real phys_lo[AMREX_SPACEDIM], phys_hi[AMREX_SPACEDIM];
1138 for (int d = 0; d < AMREX_SPACEDIM; ++d) {
1139 phys_lo[d] = plo_arr[d] + (tile_box.smallEnd(d) - ng) * dx_arr[d];
1140 phys_hi[d] = plo_arr[d] + (tile_box.bigEnd(d) + 1 + ng) * dx_arr[d];
1141 nbins[d] = std::max(1, static_cast<int>(
1142 std::ceil(static_cast<amrex::Real>(phys_hi[d] - phys_lo[d]) / bin_size)));
1143 }
1144
1147 for (int d = 0; d < AMREX_SPACEDIM; ++d) {
1148 fine_dxi[d] = static_cast<Real>(nbins[d]) / (phys_hi[d] - phys_lo[d]);
1149 fine_plo[d] = phys_lo[d];
1150 }
1151
1152 Dim3 fine_lo{.x=0, .y=0, .z=0};
1153 Dim3 fine_hi{.x=nbins[0]-1,
1154 .y=AMREX_D_PICK(0, nbins[1]-1, nbins[1]-1),
1155 .z=AMREX_D_PICK(0, 0, nbins[2]-1)};
1156
1157 Gpu::DeviceVector<int> off_bins_v;
1162
1163 off_bins_v.push_back(0);
1164 off_bins_v.push_back(AMREX_D_TERM(nbins[0], * nbins[1], * nbins[2]));
1165 lo_v.push_back(fine_lo);
1166 hi_v.push_back(fine_hi);
1167 dxi_v.push_back(fine_dxi);
1168 plo_v.push_back(fine_plo);
1169
1170 // num_cells=1: +/-1 bin in each direction. Caller must choose
1171 // bin_size >= interaction radius for completeness.
1172 neighbor_lists[lev][index].build(ptile, other_ptile,
1173 check_pair,
1174 off_bins_v, dxi_v, plo_v, lo_v, hi_v, 1);
1175 }
1176 }
1177}
1178
1179template <typename T_ParticleType, int NArrayReal, int NArrayInt,
1180 template<class> class Allocator, class CellAssignor>
1181template <class CheckPair>
1182void
1184buildNeighborList (CheckPair const& check_pair, int type_ind, int* ref_ratio,
1185 int num_bin_types, bool /*sort*/)
1186{
1187 AMREX_ASSERT(numParticlesOutOfRange(*this, m_num_neighbor_cells) == 0);
1188
1189 if (num_bin_types == 1) { AMREX_ASSERT(ref_ratio[0] == 1); }
1190
1191 BL_PROFILE("NeighborParticleContainer::buildNeighborList");
1192
1193 resizeContainers(this->numLevels());
1194
1195 for (int lev = 0; lev < this->numLevels(); ++lev)
1196 {
1197 m_neighbor_list[lev].clear();
1198
1199 for (MyParIter pti(*this, lev); pti.isValid(); ++pti) {
1200 PairIndex index(pti.index(), pti.LocalTileIndex());
1201 m_neighbor_list[lev][index];
1202 }
1203
1204#ifndef AMREX_USE_GPU
1205 neighbor_list[lev].clear();
1206 for (MyParIter pti(*this, lev); pti.isValid(); ++pti) {
1207 PairIndex index(pti.index(), pti.LocalTileIndex());
1208 neighbor_list[lev][index];
1209 }
1210#endif
1211
1212 auto& plev = this->GetParticles(lev);
1213 const auto& geom = this->Geom(lev);
1214
1215#ifdef AMREX_USE_OMP
1216#pragma omp parallel if (Gpu::notInLaunchRegion())
1217#endif
1218 for (MyParIter pti(*this, lev); pti.isValid(); ++pti)
1219 {
1220 int gid = pti.index();
1221 int tid = pti.LocalTileIndex();
1222 auto index = std::make_pair(gid, tid);
1223 auto& ptile = plev[index];
1224
1225 if (ptile.numParticles() == 0) { continue; }
1226
1227 Box bx = pti.tilebox();
1228 int ng = 1;
1229
1230 auto& soa = pti.GetStructOfArrays();
1231 auto TypeVec = soa.GetIntData(type_ind);
1232 int* bin_type_array = TypeVec.data();
1233
1234 Gpu::DeviceVector<int> off_bins_v(num_bin_types+1,0);
1235 Gpu::DeviceVector<int> nbins_v(num_bin_types+1,0);
1236 Gpu::DeviceVector<Dim3> lo_v(num_bin_types);
1237 Gpu::DeviceVector<Dim3> hi_v(num_bin_types);
1240
1241 for (int type(0); type<num_bin_types; ++type) {
1242 // Domain, RB, Coord, Per
1243 Box dom = geom.Domain();
1244 const Real* plo = geom.ProbLo();
1245 const Real* phi = geom.ProbHi();
1246 auto lcoord = geom.Coord();
1247 Array<int,AMREX_SPACEDIM> lper = geom.isPeriodic();
1248
1249 // Refined tile box and domain
1250 Box lbx( bx.smallEnd(), bx.bigEnd(), bx.ixType() );
1251 Box ldom( dom.smallEnd(),dom.bigEnd(),dom.ixType() );
1252 lbx.refine( ref_ratio[type] );
1253 ldom.refine( ref_ratio[type] );
1254
1255 // Local copy of RB for refined geom
1256 RealBox lrb(plo,phi);
1257
1258 // New geometry with refined domain
1259 Geometry lgeom(ldom,lrb,lcoord,lper);
1260
1261 // Grow for ghost cells
1262 int NGhost = ref_ratio[type]*m_num_neighbor_cells;
1263 lbx.grow(NGhost);
1264
1265 // Store for memcpy
1266 auto nbins = int(lbx.numPts());
1267 Dim3 lo = lbound( lbx );
1268 Dim3 hi = ubound( lbx );
1269
1270 auto dxInv = lgeom.InvCellSizeArray();
1271 auto ploa = lgeom.ProbLoArray();
1272
1273 Gpu::htod_memcpy_async( dxi_v.data() + type, dxInv.data(), sizeof(dxInv) );
1274 Gpu::htod_memcpy_async( plo_v.data() + type, ploa.data() , sizeof(ploa) );
1275 Gpu::htod_memcpy_async( lo_v.data() + type, &lo , sizeof(lo) );
1276 Gpu::htod_memcpy_async( hi_v.data() + type, &hi , sizeof(hi) );
1277 Gpu::htod_memcpy_async( nbins_v.data() + type, &nbins , sizeof(nbins) );
1278 }
1279
1280 Gpu::exclusive_scan(nbins_v.begin(), nbins_v.end(), off_bins_v.begin());
1281
1282 m_neighbor_list[lev][index].build(ptile,
1283 check_pair,
1284 off_bins_v, dxi_v, plo_v, lo_v, hi_v,
1285 ng, num_bin_types, bin_type_array);
1286
1287#ifndef AMREX_USE_GPU
1288 BL_PROFILE_VAR("CPU_CopyNeighborList()",CPUCNL);
1289
1290 const auto& counts = m_neighbor_list[lev][index].GetCounts();
1291 const auto& list = m_neighbor_list[lev][index].GetList();
1292
1293 int li = 0;
1294 for (int i = 0; i < ptile.numParticles(); ++i) {
1295 auto cnt = counts[i];
1296 neighbor_list[lev][index].push_back(cnt);
1297 for (size_t j = 0; j < cnt; ++j) {
1298 neighbor_list[lev][index].push_back(list[li++]+1);
1299 }
1300 }
1301
1302 BL_PROFILE_VAR_STOP(CPUCNL);
1303#endif
1304 } //ParIter
1305 } //Lev
1306}
1307
1308template <typename T_ParticleType, int NArrayReal, int NArrayInt,
1309 template<class> class Allocator, class CellAssignor>
1310template <class CheckPair>
1311void
1313selectActualNeighbors (CheckPair const& check_pair, int num_cells)
1314{
1315 BL_PROFILE("NeighborParticleContainer::selectActualNeighbors");
1316 const auto& geom_fine = this->Geom(0);
1317 const auto& ba_fine = this->ParticleBoxArray(0);
1318 if (ba_fine.size() == 1 && !geom_fine.isAnyPeriodic()) {
1319 return;
1320 }
1321
1322 for (int lev = 0; lev < this->numLevels(); ++lev)
1323 {
1324 // clear previous neighbor particle ids
1325 if (!m_boundary_particle_ids.empty()) {
1326 for (auto& keyval: m_boundary_particle_ids[lev]) {
1327 keyval.second.clear();
1328 }
1329 }
1330
1331 for (MyParIter pti(*this, lev); pti.isValid(); ++pti) {
1332 PairIndex index(pti.index(), pti.LocalTileIndex());
1333
1334 // id of actual particles that need to be sent
1335 m_boundary_particle_ids[lev][index];
1336 m_boundary_particle_ids[lev][index].resize(pti.numNeighborParticles());
1337 auto* p_boundary_particle_ids = m_boundary_particle_ids[lev][index].dataPtr();
1338
1339 auto const& ptile = this->ParticlesAt(lev, pti);
1340 auto ptile_data = ptile.getConstParticleTileData();
1341
1342 Box box = pti.tilebox();
1343 Box grownBox = box;
1344 grownBox.grow(computeRefFac(0, lev).max()*m_num_neighbor_cells);
1345 const auto lo = lbound(grownBox);
1346 const auto hi = ubound(grownBox);
1347
1348 const auto& geom = this->Geom(lev);
1349 const auto domain = geom.Domain();
1350 const auto dxi = geom.InvCellSizeArray();
1351 const auto plo = geom.ProbLoArray();
1352
1353 const size_t np_real = pti.numRealParticles();
1354 const size_t np_total = ptile.numTotalParticles();
1355
1356 DenseBins<decltype(ptile_data)> bins;
1357 bins.build(np_total, ptile_data, grownBox,
1358 [=] AMREX_GPU_DEVICE (auto const& p) noexcept -> IntVect
1359 {
1360 AMREX_D_TERM(int i = static_cast<int>(amrex::Math::floor((p.pos(0)-plo[0])*dxi[0]) - lo.x);,
1361 int j = static_cast<int>(amrex::Math::floor((p.pos(1)-plo[1])*dxi[1]) - lo.y);,
1362 int k = static_cast<int>(amrex::Math::floor((p.pos(2)-plo[2])*dxi[2]) - lo.z));
1363 AMREX_D_TERM(AMREX_ASSERT(i >= 0);, AMREX_ASSERT(j >= 0);, AMREX_ASSERT(k >= 0));
1364
1365 return IntVect(AMREX_D_DECL(i, j, k));
1366 });
1367
1368 auto pperm = bins.permutationPtr();
1369 auto poffset = bins.offsetsPtr();
1370
1371 Gpu::Buffer<unsigned int> np_boundary({0});
1372 unsigned int* p_np_boundary = np_boundary.data();
1373
1374 AMREX_FOR_1D ( np_real, i,
1375 {
1376 IntVect iv = getParticleCell(ptile_data, i, plo, dxi, domain);
1377 iv -= grownBox.smallEnd();
1378 auto iv3 = iv.dim3();
1379
1380 int ix = iv3.x;
1381 int iy = iv3.y;
1382 int iz = iv3.z;
1383
1384 int nx = hi.x-lo.x+1;
1385 int ny = hi.y-lo.y+1;
1386 int nz = hi.z-lo.z+1;
1387
1388 bool isActualNeighbor = false;
1389 // These loops mirror the way the neighbor particles were originally constructed, except
1390 // it adds a call to check_pair. Thus the maximum number value of "loc" below is the
1391 // number of neighbors minus 1, and the buffer pointed to by p_boundary_particle_ids
1392 // will not be overwritten.
1393 for (int kk = amrex::max(iz-num_cells, 0); kk <= amrex::min(iz+num_cells, nz-1); ++kk) {
1394 for (int jj = amrex::max(iy-num_cells, 0); jj <= amrex::min(iy+num_cells, ny-1); ++jj) {
1395 for (int ii = amrex::max(ix-num_cells, 0); ii <= amrex::min(ix+num_cells, nx-1); ++ii) {
1396 if (isActualNeighbor) { break; }
1397 int nbr_cell_id = (kk * ny + jj) * nx + ii;
1398 for (auto p = poffset[nbr_cell_id]; p < poffset[nbr_cell_id+1]; ++p) {
1399 if (pperm[p] == int(i)) { continue; }
1400 if (detail::call_check_pair(check_pair, ptile_data, ptile_data, i, pperm[p])) {
1401 IntVect cell_ijk = getParticleCell(ptile_data, pperm[p], plo, dxi, domain);
1402 if (!box.contains(cell_ijk)) {
1403 unsigned int loc = Gpu::Atomic::Add(p_np_boundary, 1U);
1404 p_boundary_particle_ids[loc] = i;
1405 isActualNeighbor = true;
1406 break;
1407 }
1408 }// end if check_pair
1409 }
1410 }
1411 }
1412 }
1413 });// end amrex_for_1d
1414
1415 unsigned int* p_np_boundary_h = np_boundary.copyToHost();
1416 m_boundary_particle_ids[lev][index].resize(*p_np_boundary_h);
1417
1418 }// end mypariter
1419 }// end lev
1420}
1421template <typename T_ParticleType, int NArrayReal, int NArrayInt,
1422 template<class> class Allocator, class CellAssignor>
1423void
1426{
1427 BL_PROFILE("NeighborParticleContainer::printNeighborList");
1428
1429 for (int lev = 0; lev < this->numLevels(); ++lev)
1430 {
1431 for(MFIter mfi = this->MakeMFIter(lev); mfi.isValid(); ++mfi)
1432 {
1433 int gid = mfi.index();
1434 int tid = mfi.LocalTileIndex();
1435 auto index = std::make_pair(gid, tid);
1436 m_neighbor_list[lev][index].print();
1437 }
1438 }
1439}
1440
1441template <typename T_ParticleType, int NArrayReal, int NArrayInt,
1442 template<class> class Allocator, class CellAssignor>
1443void
1445resizeContainers (int num_levels)
1446{
1447 this->reserveData();
1448 this->resizeData();
1449 if ( std::ssize(neighbors) <= num_levels )
1450 {
1451 neighbors.resize(num_levels);
1452 m_neighbor_list.resize(num_levels);
1453 neighbor_list.resize(num_levels);
1454 mask_ptr.resize(num_levels);
1455 buffer_tag_cache.resize(num_levels);
1456 local_neighbor_sizes.resize(num_levels);
1457 if ( enableInverse() ) { inverse_tags.resize(num_levels); }
1458 }
1459
1460 AMREX_ASSERT((neighbors.size() == m_neighbor_list.size()) &&
1461 (neighbors.size() == mask_ptr.size() ) );
1462}
1463
1464}
#define BL_PROFILE(a)
Definition AMReX_BLProfiler.H:551
#define BL_PROFILE_VAR_STOP(vname)
Definition AMReX_BLProfiler.H:563
#define BL_PROFILE_VAR(fname, vname)
Definition AMReX_BLProfiler.H:560
#define AMREX_ALWAYS_ASSERT_WITH_MESSAGE(EX, MSG)
Definition AMReX_BLassert.H:49
#define AMREX_ASSERT(EX)
Definition AMReX_BLassert.H:38
#define AMREX_FOR_1D(...)
Definition AMReX_GpuLaunchMacrosC.nolint.H:97
#define AMREX_GPU_DEVICE
Definition AMReX_GpuQualifiers.H:18
Array4< int const > mask
Definition AMReX_InterpFaceRegister.cpp:93
#define AMREX_D_TERM(a, b, c)
Definition AMReX_SPACE.H:172
#define AMREX_D_PICK(a, b, c)
Definition AMReX_SPACE.H:173
#define AMREX_D_DECL(a, b, c)
Definition AMReX_SPACE.H:171
A FortranArrayBox(FAB)-like object.
Definition AMReX_BaseFab.H:194
A collection of Boxes stored in an Array.
Definition AMReX_BoxArray.H:564
std::vector< std::pair< int, Box > > intersections(const Box &bx) const
Return intersections of Box and BoxArray.
Definition AMReX_BoxArray.cpp:1186
static bool SameRefs(const BoxArray &lhs, const BoxArray &rhs)
whether two BoxArrays share the same data
Definition AMReX_BoxArray.H:837
__host__ __device__ BoxND & grow(int i) noexcept
Definition AMReX_Box.H:649
__host__ __device__ const IntVectND< dim > & bigEnd() const &noexcept
Return the inclusive upper bound of the box.
Definition AMReX_Box.H:124
__host__ __device__ Long numPts() const noexcept
Return the number of points contained in the BoxND.
Definition AMReX_Box.H:364
__host__ __device__ bool contains(const IntVectND< dim > &p) const noexcept
Return true if argument is contained within BoxND.
Definition AMReX_Box.H:216
__host__ __device__ IndexTypeND< dim > ixType() const noexcept
Return the indexing type.
Definition AMReX_Box.H:136
__host__ __device__ BoxND & coarsen(int ref_ratio) noexcept
Coarsen BoxND by given (positive) refinement ratio. NOTE: if type(dir) = CELL centered: lo <- lo/rati...
Definition AMReX_Box.H:730
__host__ __device__ BoxND & refine(int ref_ratio) noexcept
Refine BoxND by given (positive) refinement ratio. NOTE: if type(dir) = CELL centered: lo <- lo*ratio...
Definition AMReX_Box.H:706
__host__ __device__ Long index(const IntVectND< dim > &v) const noexcept
Return offset of point from smallend; i.e. index(smallend) -> 0, bigend would return numPts()-1....
Definition AMReX_Box.H:1063
__host__ __device__ void next(IntVectND< dim > &) const noexcept
Step through the rectangle. It is a runtime error to give a point not inside rectangle....
Definition AMReX_Box.H:1130
__host__ __device__ const IntVectND< dim > & smallEnd() const &noexcept
Return the inclusive lower bound of the box.
Definition AMReX_Box.H:112
const Real * CellSize() const noexcept
Returns the cellsize for each coordinate direction.
Definition AMReX_CoordSys.H:71
GpuArray< Real, 3 > InvCellSizeArray() const noexcept
Definition AMReX_CoordSys.H:87
A container for storing items in a set of bins.
Definition AMReX_DenseBins.H:77
void build(N nitems, const_pointer_input_type v, const Box &bx, F &&f)
Populate the bins with a set of items.
Definition AMReX_DenseBins.H:130
Calculates the distribution of FABs to MPI processes.
Definition AMReX_DistributionMapping.H:43
static bool SameRefs(const DistributionMapping &lhs, const DistributionMapping &rhs)
Definition AMReX_DistributionMapping.H:189
Rectangular problem domain geometry.
Definition AMReX_Geometry.H:75
Periodicity periodicity() const noexcept
Definition AMReX_Geometry.H:361
GpuArray< Real, 3 > ProbLoArray() const noexcept
Definition AMReX_Geometry.H:192
Definition AMReX_GpuBuffer.H:24
T const * data() const noexcept
Definition AMReX_GpuBuffer.H:51
__host__ __device__ constexpr Dim3 dim3() const noexcept
Definition AMReX_IntVect.H:262
__host__ static __device__ constexpr IntVectND< dim > TheZeroVector() noexcept
This static member function returns a reference to a constant IntVectND object, all of whose dim argu...
Definition AMReX_IntVect.H:771
__host__ __device__ constexpr int max() const noexcept
maximum (no absolute values) value
Definition AMReX_IntVect.H:313
Iterator for looping ever tiles and boxes of amrex::FabArray based containers.
Definition AMReX_MFIter.H:88
bool isValid() const noexcept
Is the iterator valid i.e. is it associated with a FAB?
Definition AMReX_MFIter.H:172
Definition AMReX_NeighborList.H:309
Definition AMReX_NeighborParticles.H:39
std::pair< int, int > PairIndex
Definition AMReX_NeighborParticles.H:210
void selectActualNeighbors(CheckPair const &check_pair, int num_cells=1)
Definition AMReX_NeighborParticlesI.H:1313
static bool enable_inverse
Definition AMReX_NeighborParticles.H:480
void buildNeighborList(CheckPair const &check_pair, bool sort=false)
Definition AMReX_NeighborParticlesI.H:818
void printNeighborList()
Definition AMReX_NeighborParticlesI.H:1425
static bool use_mask
Definition AMReX_NeighborParticles.H:478
void initializeCommComps()
Definition AMReX_NeighborParticlesI.H:60
void getNeighborTags(Vector< NeighborCopyTag > &tags, const P &p, int nGrow, const NeighborCopyTag &src_tag, const MyParIter &pti, amrex::Real search_radius=amrex::Real(0))
Definition AMReX_NeighborParticlesI.H:603
typename ParticleContainerType::ParIterType MyParIter
Definition AMReX_NeighborParticles.H:209
void resizeContainers(int num_levels)
Definition AMReX_NeighborParticlesI.H:1445
Dynamically allocated vector for trivially copyable data.
Definition AMReX_PODVector.H:308
iterator begin() noexcept
Definition AMReX_PODVector.H:674
iterator end() noexcept
Definition AMReX_PODVector.H:678
T * data() noexcept
Definition AMReX_PODVector.H:666
void push_back(const T &a_value)
Definition AMReX_PODVector.H:629
Definition AMReX_ParGDB.H:13
A distributed container for Particles sorted onto the levels, grids, and tiles of a block-structured ...
Definition AMReX_ParticleContainer.H:149
This provides length of period for periodic domains. 0 means it is not periodic in that direction....
Definition AMReX_Periodicity.H:17
std::vector< IntVect > shiftIntVect(IntVect const &nghost=IntVect(0)) const
Definition AMReX_Periodicity.cpp:8
bool isAnyPeriodic() const noexcept
Definition AMReX_Periodicity.H:22
bool isPeriodic(int dir) const noexcept
Definition AMReX_Periodicity.H:26
A Box with real dimensions.
Definition AMReX_RealBox.H:28
This class is a thin wrapper around std::vector. Unlike vector, Vector::operator[] provides bound che...
Definition AMReX_Vector.H:29
Long size() const noexcept
Definition AMReX_Vector.H:54
amrex_real Real
Floating Point Type for Fields.
Definition AMReX_REAL.H:79
amrex_particle_real ParticleReal
Floating Point Type for Particles.
Definition AMReX_REAL.H:90
OutIter exclusive_scan(InIter begin, InIter end, OutIter result)
Definition AMReX_Scan.H:1190
__host__ __device__ Dim3 ubound(Array4< T > const &a) noexcept
Return the inclusive upper bounds of an Array4 in Dim3 form.
Definition AMReX_Array4.H:1359
__host__ __device__ Dim3 lbound(Array4< T > const &a) noexcept
Return the inclusive lower bounds of an Array4 in Dim3 form.
Definition AMReX_Array4.H:1345
__host__ __device__ BoxND< dim > grow(const BoxND< dim > &b, int i) noexcept
Grow BoxND in all directions by given amount.
Definition AMReX_Box.H:1289
std::array< T, N > Array
Definition AMReX_Array.H:26
__host__ __device__ AMREX_FORCE_INLINE T Add(T *sum, T value) noexcept
Definition AMReX_GpuAtomic.H:200
void htod_memcpy_async(void *p_d, const void *p_h, const std::size_t sz) noexcept
Definition AMReX_GpuDevice.H:421
constexpr int get_thread_num()
Definition AMReX_OpenMP.H:37
constexpr int get_max_threads()
Definition AMReX_OpenMP.H:36
int MyProcSub() noexcept
my sub-rank in current frame
Definition AMReX_ParallelContext.H:76
int global_to_local_rank(int rank) noexcept
Definition AMReX_ParallelContext.H:98
Definition AMReX_Amr.cpp:50
__host__ __device__ void ignore_unused(const Ts &...)
This shuts up the compiler about unused variables.
Definition AMReX.H:139
__host__ __device__ int getTileIndex(const IntVect &iv, const Box &box, const bool a_do_tiling, const IntVect &a_tile_size, Box &tbx)
Definition AMReX_ParticleUtil.H:191
DistributionMapping const & DistributionMap(FabArrayBase const &fa)
Definition AMReX_FabArrayBase.cpp:2867
void EnsureThreadSafeTiles(PC &pc)
Definition AMReX_ParticleUtil.H:737
__host__ __device__ Dim3 begin(BoxND< dim > const &box) noexcept
Definition AMReX_Box.H:2018
BoxND< 3 > Box
Box is an alias for amrex::BoxND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:30
__host__ __device__ constexpr const T & min(const T &a, const T &b) noexcept
Definition AMReX_Algorithm.H:25
__host__ __device__ BoxND< dim > shift(const BoxND< dim > &b, int dir, int nzones) noexcept
Return a BoxND with indices shifted by nzones in dir direction.
Definition AMReX_Box.H:1504
IntVectND< 3 > IntVect
IntVect is an alias for amrex::IntVectND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:33
__host__ __device__ constexpr const T & max(const T &a, const T &b) noexcept
Definition AMReX_Algorithm.H:45
int numParticlesOutOfRange(Iterator const &pti, int nGrow)
Returns the number of particles that are more than nGrow cells from the box correspond to the input i...
Definition AMReX_ParticleUtil.H:36
bool SameIteratorsOK(const PC1 &pc1, const PC2 &pc2)
Definition AMReX_ParticleUtil.H:725
void Abort(const std::string &msg)
Print out message to cerr and exit via abort().
Definition AMReX.cpp:241
const int[]
Definition AMReX_BLProfiler.cpp:1664
IntVect computeRefFac(const ParGDBBase *a_gdb, int src_lev, int lev)
Definition AMReX_ParticleUtil.cpp:6
void RemoveDuplicates(Vector< T > &vec)
Definition AMReX_Vector.H:210
__host__ __device__ Dim3 end(BoxND< dim > const &box) noexcept
Definition AMReX_Box.H:2028
__host__ __device__ IntVect getParticleCell(P const &p, amrex::GpuArray< amrex::Real, 3 > const &plo, amrex::GpuArray< amrex::Real, 3 > const &dxi) noexcept
Returns the cell index for a given particle using the provided lower bounds and cell sizes.
Definition AMReX_ParticleUtil.H:343
BoxArray const & boxArray(FabArrayBase const &fa)
Definition AMReX_FabArrayBase.cpp:2862
Definition AMReX_Dim3.H:13
int x
Definition AMReX_Dim3.H:13
Fixed-size array that can be used on GPU.
Definition AMReX_Array.H:43