Line data Source code
1 :
2 : #include <AMReX_FBI.H>
3 : #include <AMReX_PCI.H>
4 :
5 : template <class FAB>
6 : template <typename BUF, class F, std::enable_if_t<IsBaseFab<F>::value,int>Z>
7 : void
8 275010 : FabArray<FAB>::FBEP_nowait (int scomp, int ncomp, const IntVect& nghost,
9 : const Periodicity& period, bool cross,
10 : bool enforce_periodicity_only,
11 : bool override_sync)
12 : {
13 : BL_PROFILE_SYNC_START_TIMED("SyncBeforeComms: FB");
14 : BL_PROFILE("FillBoundary_nowait()");
15 :
16 : AMREX_ASSERT_WITH_MESSAGE(!fbd, "FillBoundary_nowait() called when comm operation already in progress.");
17 : AMREX_ASSERT(!enforce_periodicity_only || !override_sync);
18 :
19 : bool work_to_do;
20 275010 : if (enforce_periodicity_only) {
21 0 : work_to_do = period.isAnyPeriodic();
22 275010 : } else if (override_sync) {
23 33398 : work_to_do = (nghost.max() > 0) || !is_cell_centered();
24 : } else {
25 241612 : work_to_do = nghost.max() > 0;
26 : }
27 550020 : if (!work_to_do) { return; }
28 :
29 275010 : const FB& TheFB = getFB(nghost, period, cross, enforce_periodicity_only, override_sync);
30 :
31 275010 : if (ParallelContext::NProcsSub() == 1)
32 : {
33 : //
34 : // There can only be local work to do.
35 : //
36 275010 : int N_locs = (*TheFB.m_LocTags).size();
37 275010 : if (N_locs == 0) { return; }
38 : #ifdef AMREX_USE_GPU
39 : if (Gpu::inLaunchRegion())
40 : {
41 : #if defined(__CUDACC__) && defined(AMREX_USE_CUDA)
42 : if (Gpu::inGraphRegion())
43 : {
44 : FB_local_copy_cuda_graph_1(TheFB, scomp, ncomp);
45 : }
46 : else
47 : #endif
48 : {
49 : FB_local_copy_gpu(TheFB, scomp, ncomp);
50 : }
51 : }
52 : else
53 : #endif
54 : {
55 132 : FB_local_copy_cpu(TheFB, scomp, ncomp);
56 : }
57 :
58 132 : return;
59 : }
60 :
61 : #ifdef BL_USE_MPI
62 :
63 : //
64 : // Do this before prematurely exiting if running in parallel.
65 : // Otherwise sequence numbers will not match across MPI processes.
66 : //
67 0 : int SeqNum = ParallelDescriptor::SeqNum();
68 :
69 0 : const int N_locs = TheFB.m_LocTags->size();
70 0 : const int N_rcvs = TheFB.m_RcvTags->size();
71 0 : const int N_snds = TheFB.m_SndTags->size();
72 :
73 0 : if (N_locs == 0 && N_rcvs == 0 && N_snds == 0) {
74 : // No work to do.
75 0 : return;
76 : }
77 :
78 0 : fbd = std::make_unique<FBData<FAB>>();
79 0 : fbd->fb = &TheFB;
80 0 : fbd->scomp = scomp;
81 0 : fbd->ncomp = ncomp;
82 0 : fbd->tag = SeqNum;
83 :
84 : //
85 : // Post rcvs. Allocate one chunk of space to hold'm all.
86 : //
87 :
88 0 : if (N_rcvs > 0) {
89 0 : PostRcvs<BUF>(*TheFB.m_RcvTags, fbd->the_recv_data,
90 0 : fbd->recv_data, fbd->recv_size, fbd->recv_from, fbd->recv_reqs,
91 : ncomp, SeqNum);
92 0 : fbd->recv_stat.resize(N_rcvs);
93 : }
94 :
95 : //
96 : // Post send's
97 : //
98 0 : char*& the_send_data = fbd->the_send_data;
99 0 : Vector<char*> & send_data = fbd->send_data;
100 0 : Vector<std::size_t> send_size;
101 0 : Vector<int> send_rank;
102 0 : Vector<MPI_Request>& send_reqs = fbd->send_reqs;
103 0 : Vector<const CopyComTagsContainer*> send_cctc;
104 :
105 0 : if (N_snds > 0)
106 : {
107 0 : PrepareSendBuffers<BUF>(*TheFB.m_SndTags, the_send_data, send_data, send_size, send_rank,
108 : send_reqs, send_cctc, ncomp);
109 :
110 : #ifdef AMREX_USE_GPU
111 : if (Gpu::inLaunchRegion())
112 : {
113 : #if defined(__CUDACC__) && defined(AMREX_USE_CUDA)
114 : if (Gpu::inGraphRegion()) {
115 : FB_pack_send_buffer_cuda_graph(TheFB, scomp, ncomp, send_data, send_size, send_cctc);
116 : }
117 : else
118 : #endif
119 : {
120 : pack_send_buffer_gpu<BUF>(*this, scomp, ncomp, send_data, send_size, send_cctc);
121 : }
122 : }
123 : else
124 : #endif
125 : {
126 0 : pack_send_buffer_cpu<BUF>(*this, scomp, ncomp, send_data, send_size, send_cctc);
127 : }
128 :
129 : AMREX_ASSERT(send_reqs.size() == N_snds);
130 0 : PostSnds(send_data, send_size, send_rank, send_reqs, SeqNum);
131 : }
132 :
133 0 : FillBoundary_test();
134 :
135 : //
136 : // Do the local work. Hope for a bit of communication/computation overlap.
137 : //
138 0 : if (N_locs > 0)
139 : {
140 : #ifdef AMREX_USE_GPU
141 : if (Gpu::inLaunchRegion())
142 : {
143 : #if defined(__CUDACC__) && defined(AMREX_USE_CUDA)
144 : if (Gpu::inGraphRegion()) {
145 : FB_local_copy_cuda_graph_n(TheFB, scomp, ncomp);
146 : }
147 : else
148 : #endif
149 : {
150 : FB_local_copy_gpu(TheFB, scomp, ncomp);
151 : }
152 : }
153 : else
154 : #endif
155 : {
156 0 : FB_local_copy_cpu(TheFB, scomp, ncomp);
157 : }
158 :
159 0 : FillBoundary_test();
160 : }
161 :
162 : #endif /*BL_USE_MPI*/
163 : }
164 :
165 : template <class FAB>
166 : template <typename BUF, class F, std::enable_if_t<IsBaseFab<F>::value,int>Z>
167 : void
168 275010 : FabArray<FAB>::FillBoundary_finish ()
169 : {
170 : #ifdef AMREX_USE_MPI
171 :
172 : BL_PROFILE("FillBoundary_finish()");
173 : BL_PROFILE_SYNC_STOP();
174 :
175 550020 : if (!fbd) { n_filled = IntVect::TheZeroVector(); return; }
176 :
177 0 : const FB* TheFB = fbd->fb;
178 0 : const auto N_rcvs = static_cast<int>(TheFB->m_RcvTags->size());
179 0 : if (N_rcvs > 0)
180 : {
181 0 : Vector<const CopyComTagsContainer*> recv_cctc(N_rcvs,nullptr);
182 0 : for (int k = 0; k < N_rcvs; k++)
183 : {
184 0 : if (fbd->recv_size[k] > 0)
185 : {
186 0 : auto const& cctc = TheFB->m_RcvTags->at(fbd->recv_from[k]);
187 0 : recv_cctc[k] = &cctc;
188 : }
189 : }
190 :
191 0 : int actual_n_rcvs = N_rcvs - std::count(fbd->recv_data.begin(), fbd->recv_data.end(), nullptr);
192 :
193 0 : if (actual_n_rcvs > 0) {
194 0 : ParallelDescriptor::Waitall(fbd->recv_reqs, fbd->recv_stat);
195 : #ifdef AMREX_DEBUG
196 : if (!CheckRcvStats(fbd->recv_stat, fbd->recv_size, fbd->tag))
197 : {
198 : amrex::Abort("FillBoundary_finish failed with wrong message size");
199 : }
200 : #endif
201 : }
202 :
203 0 : bool is_thread_safe = TheFB->m_threadsafe_rcv;
204 :
205 : #ifdef AMREX_USE_GPU
206 : if (Gpu::inLaunchRegion())
207 : {
208 : #if defined(__CUDACC__) && defined(AMREX_USE_CUDA)
209 : if (Gpu::inGraphRegion())
210 : {
211 : FB_unpack_recv_buffer_cuda_graph(*TheFB, fbd->scomp, fbd->ncomp,
212 : fbd->recv_data, fbd->recv_size,
213 : recv_cctc, is_thread_safe);
214 : }
215 : else
216 : #endif
217 : {
218 : unpack_recv_buffer_gpu<BUF>(*this, fbd->scomp, fbd->ncomp, fbd->recv_data, fbd->recv_size,
219 : recv_cctc, FabArrayBase::COPY, is_thread_safe);
220 : }
221 : }
222 : else
223 : #endif
224 : {
225 0 : unpack_recv_buffer_cpu<BUF>(*this, fbd->scomp, fbd->ncomp, fbd->recv_data, fbd->recv_size,
226 : recv_cctc, FabArrayBase::COPY, is_thread_safe);
227 : }
228 :
229 0 : if (fbd->the_recv_data)
230 : {
231 0 : amrex::The_Comms_Arena()->free(fbd->the_recv_data);
232 0 : fbd->the_recv_data = nullptr;
233 : }
234 : }
235 :
236 0 : const auto N_snds = static_cast<int>(TheFB->m_SndTags->size());
237 0 : if (N_snds > 0) {
238 0 : Vector<MPI_Status> stats(fbd->send_reqs.size());
239 0 : ParallelDescriptor::Waitall(fbd->send_reqs, stats);
240 0 : amrex::The_Comms_Arena()->free(fbd->the_send_data);
241 0 : fbd->the_send_data = nullptr;
242 : }
243 :
244 0 : fbd.reset();
245 :
246 : #endif
247 : }
248 :
249 : // \cond CODEGEN
250 : template <class FAB>
251 : void
252 6476 : FabArray<FAB>::ParallelCopy (const FabArray<FAB>& src,
253 : int scomp,
254 : int dcomp,
255 : int ncomp,
256 : const IntVect& snghost,
257 : const IntVect& dnghost,
258 : const Periodicity& period,
259 : CpOp op,
260 : const FabArrayBase::CPC * a_cpc)
261 : {
262 : BL_PROFILE("FabArray::ParallelCopy()");
263 :
264 6476 : ParallelCopy_nowait(src, scomp, dcomp, ncomp, snghost, dnghost, period, op, a_cpc);
265 6476 : ParallelCopy_finish();
266 6476 : }
267 :
268 : template <class FAB>
269 : void
270 0 : FabArray<FAB>::ParallelCopyToGhost (const FabArray<FAB>& src,
271 : int scomp,
272 : int dcomp,
273 : int ncomp,
274 : const IntVect& snghost,
275 : const IntVect& dnghost,
276 : const Periodicity& period)
277 : {
278 : BL_PROFILE("FabArray::ParallelCopyToGhost()");
279 :
280 0 : ParallelCopy_nowait(src, scomp, dcomp, ncomp, snghost, dnghost, period,
281 : FabArrayBase::COPY, nullptr, true);
282 0 : ParallelCopy_finish();
283 0 : }
284 :
285 : template <class FAB>
286 : void
287 : FabArray<FAB>::ParallelCopyToGhost_nowait (const FabArray<FAB>& src,
288 : int scomp,
289 : int dcomp,
290 : int ncomp,
291 : const IntVect& snghost,
292 : const IntVect& dnghost,
293 : const Periodicity& period)
294 : {
295 : ParallelCopy_nowait(src, scomp, dcomp, ncomp, snghost, dnghost, period,
296 : FabArrayBase::COPY, nullptr, true);
297 : }
298 :
299 : template <class FAB>
300 : void
301 : FabArray<FAB>::ParallelCopyToGhost_finish ()
302 : {
303 : ParallelCopy_finish();
304 : }
305 :
306 :
307 : template <class FAB>
308 : void
309 6476 : FabArray<FAB>::ParallelCopy_nowait (const FabArray<FAB>& src,
310 : int scomp,
311 : int dcomp,
312 : int ncomp,
313 : const IntVect& snghost,
314 : const IntVect& dnghost,
315 : const Periodicity& period,
316 : CpOp op,
317 : const FabArrayBase::CPC * a_cpc,
318 : bool to_ghost_cells_only)
319 : {
320 : BL_PROFILE_SYNC_START_TIMED("SyncBeforeComms: PC");
321 : BL_PROFILE("FabArray::ParallelCopy_nowait()");
322 :
323 : AMREX_ASSERT_WITH_MESSAGE(!pcd, "ParallelCopy_nowait() called when comm operation already in progress.");
324 :
325 6476 : if (empty() || src.empty()) {
326 6476 : return;
327 : }
328 :
329 : BL_ASSERT(op == FabArrayBase::COPY || op == FabArrayBase::ADD);
330 : BL_ASSERT(boxArray().ixType() == src.boxArray().ixType());
331 : BL_ASSERT(src.nGrowVect().allGE(snghost));
332 : BL_ASSERT( nGrowVect().allGE(dnghost));
333 :
334 6476 : n_filled = dnghost;
335 :
336 16852 : if ((src.boxArray().ixType().cellCentered() || op == FabArrayBase::COPY) &&
337 12776 : (boxarray == src.boxarray && distributionMap == src.distributionMap) &&
338 12600 : snghost == IntVect::TheZeroVector() &&
339 0 : dnghost == IntVect::TheZeroVector() &&
340 12952 : !period.isAnyPeriodic() && !to_ghost_cells_only)
341 : {
342 : //
343 : // Short-circuit full intersection code if we're doing copy()s or if
344 : // we're doing plus()s on cell-centered data. Don't do plus()s on
345 : // non-cell-centered data this simplistic way.
346 : //
347 0 : if (this != &src) { // avoid self copy or plus
348 0 : if (op == FabArrayBase::COPY) {
349 0 : Copy(*this, src, scomp, dcomp, ncomp, IntVect(0));
350 : } else {
351 0 : Add(*this, src, scomp, dcomp, ncomp, IntVect(0));
352 : }
353 : }
354 0 : return;
355 : }
356 :
357 6476 : const CPC& thecpc = (a_cpc) ? *a_cpc : getCPC(dnghost, src, snghost, period, to_ghost_cells_only);
358 :
359 6476 : if (ParallelContext::NProcsSub() == 1)
360 : {
361 : //
362 : // There can only be local work to do.
363 : //
364 :
365 6476 : int N_locs = (*thecpc.m_LocTags).size();
366 6476 : if (N_locs == 0) { return; }
367 : #ifdef AMREX_USE_GPU
368 : if (Gpu::inLaunchRegion())
369 : {
370 : PC_local_gpu(thecpc, src, scomp, dcomp, ncomp, op);
371 : }
372 : else
373 : #endif
374 : {
375 6476 : PC_local_cpu(thecpc, src, scomp, dcomp, ncomp, op);
376 : }
377 :
378 6476 : return;
379 : }
380 :
381 : #ifdef BL_USE_MPI
382 :
383 : //
384 : // Do this before prematurely exiting if running in parallel.
385 : // Otherwise sequence numbers will not match across MPI processes.
386 : //
387 0 : int tag = ParallelDescriptor::SeqNum();
388 :
389 0 : const int N_snds = thecpc.m_SndTags->size();
390 0 : const int N_rcvs = thecpc.m_RcvTags->size();
391 0 : const int N_locs = thecpc.m_LocTags->size();
392 :
393 0 : if (N_locs == 0 && N_rcvs == 0 && N_snds == 0) {
394 : //
395 : // No work to do.
396 : //
397 :
398 0 : return;
399 : }
400 :
401 : //
402 : // Send/Recv at most MaxComp components at a time to cut down memory usage.
403 : //
404 0 : int NCompLeft = ncomp;
405 0 : int SC = scomp, DC = dcomp, NC;
406 :
407 0 : for (int ipass = 0; ipass < ncomp; )
408 : {
409 0 : pcd = std::make_unique<PCData<FAB>>();
410 0 : pcd->cpc = &thecpc;
411 0 : pcd->src = &src;
412 0 : pcd->op = op;
413 0 : pcd->tag = tag;
414 :
415 0 : NC = std::min(NCompLeft,FabArrayBase::MaxComp);
416 0 : const bool last_iter = (NCompLeft == NC);
417 :
418 0 : pcd->SC = SC;
419 0 : pcd->DC = DC;
420 0 : pcd->NC = NC;
421 :
422 : //
423 : // Post rcvs. Allocate one chunk of space to hold'm all.
424 : //
425 0 : pcd->the_recv_data = nullptr;
426 :
427 0 : pcd->actual_n_rcvs = 0;
428 0 : if (N_rcvs > 0) {
429 0 : PostRcvs(*thecpc.m_RcvTags, pcd->the_recv_data,
430 0 : pcd->recv_data, pcd->recv_size, pcd->recv_from, pcd->recv_reqs, NC, pcd->tag);
431 0 : pcd->actual_n_rcvs = N_rcvs - std::count(pcd->recv_size.begin(), pcd->recv_size.end(), 0);
432 : }
433 :
434 : //
435 : // Post send's
436 : //
437 0 : Vector<char*> send_data;
438 0 : Vector<std::size_t> send_size;
439 0 : Vector<int> send_rank;
440 0 : Vector<const CopyComTagsContainer*> send_cctc;
441 :
442 0 : if (N_snds > 0)
443 : {
444 0 : src.PrepareSendBuffers(*thecpc.m_SndTags, pcd->the_send_data, send_data, send_size,
445 0 : send_rank, pcd->send_reqs, send_cctc, NC);
446 :
447 : #ifdef AMREX_USE_GPU
448 : if (Gpu::inLaunchRegion())
449 : {
450 : pack_send_buffer_gpu(src, SC, NC, send_data, send_size, send_cctc);
451 : }
452 : else
453 : #endif
454 : {
455 0 : pack_send_buffer_cpu(src, SC, NC, send_data, send_size, send_cctc);
456 : }
457 :
458 : AMREX_ASSERT(pcd->send_reqs.size() == N_snds);
459 0 : FabArray<FAB>::PostSnds(send_data, send_size, send_rank, pcd->send_reqs, pcd->tag);
460 : }
461 :
462 : //
463 : // Do the local work. Hope for a bit of communication/computation overlap.
464 : //
465 0 : if (N_locs > 0)
466 : {
467 : #ifdef AMREX_USE_GPU
468 : if (Gpu::inLaunchRegion())
469 : {
470 : PC_local_gpu(thecpc, src, SC, DC, NC, op);
471 : }
472 : else
473 : #endif
474 : {
475 0 : PC_local_cpu(thecpc, src, SC, DC, NC, op);
476 : }
477 : }
478 :
479 0 : if (!last_iter)
480 : {
481 0 : ParallelCopy_finish();
482 :
483 0 : SC += NC;
484 0 : DC += NC;
485 : }
486 :
487 0 : ipass += NC;
488 0 : NCompLeft -= NC;
489 : }
490 :
491 : #endif /*BL_USE_MPI*/
492 : }
493 :
494 : template <class FAB>
495 : void
496 6476 : FabArray<FAB>::ParallelCopy_finish ()
497 : {
498 :
499 : #ifdef BL_USE_MPI
500 :
501 : BL_PROFILE("FabArray::ParallelCopy_finish()");
502 : BL_PROFILE_SYNC_STOP();
503 :
504 6476 : if (!pcd) { return; }
505 :
506 0 : const CPC* thecpc = pcd->cpc;
507 :
508 0 : const auto N_snds = static_cast<int>(thecpc->m_SndTags->size());
509 0 : const auto N_rcvs = static_cast<int>(thecpc->m_RcvTags->size());
510 :
511 0 : if (N_rcvs > 0)
512 : {
513 0 : Vector<const CopyComTagsContainer*> recv_cctc(N_rcvs,nullptr);
514 0 : for (int k = 0; k < N_rcvs; ++k)
515 : {
516 0 : if (pcd->recv_size[k] > 0)
517 : {
518 0 : auto const& cctc = thecpc->m_RcvTags->at(pcd->recv_from[k]);
519 0 : recv_cctc[k] = &cctc;
520 : }
521 : }
522 :
523 0 : if (pcd->actual_n_rcvs > 0) {
524 0 : Vector<MPI_Status> stats(N_rcvs);
525 0 : ParallelDescriptor::Waitall(pcd->recv_reqs, stats);
526 : #ifdef AMREX_DEBUG
527 : if (!CheckRcvStats(stats, pcd->recv_size, pcd->tag))
528 : {
529 : amrex::Abort("ParallelCopy failed with wrong message size");
530 : }
531 : #endif
532 : }
533 :
534 0 : bool is_thread_safe = thecpc->m_threadsafe_rcv;
535 :
536 : #ifdef AMREX_USE_GPU
537 : if (Gpu::inLaunchRegion())
538 : {
539 : unpack_recv_buffer_gpu(*this, pcd->DC, pcd->NC, pcd->recv_data, pcd->recv_size,
540 : recv_cctc, pcd->op, is_thread_safe);
541 : }
542 : else
543 : #endif
544 : {
545 0 : unpack_recv_buffer_cpu(*this, pcd->DC, pcd->NC, pcd->recv_data, pcd->recv_size,
546 0 : recv_cctc, pcd->op, is_thread_safe);
547 : }
548 :
549 0 : if (pcd->the_recv_data)
550 : {
551 0 : amrex::The_Comms_Arena()->free(pcd->the_recv_data);
552 0 : pcd->the_recv_data = nullptr;
553 : }
554 : }
555 :
556 0 : if (N_snds > 0) {
557 0 : if (! thecpc->m_SndTags->empty()) {
558 0 : Vector<MPI_Status> stats(pcd->send_reqs.size());
559 0 : ParallelDescriptor::Waitall(pcd->send_reqs, stats);
560 : }
561 0 : amrex::The_Comms_Arena()->free(pcd->the_send_data);
562 0 : pcd->the_send_data = nullptr;
563 : }
564 :
565 0 : pcd.reset();
566 :
567 : #endif /*BL_USE_MPI*/
568 : }
569 :
570 : template <class FAB>
571 : void
572 : FabArray<FAB>::copyTo (FAB& dest, int scomp, int dcomp, int ncomp, int nghost) const
573 : {
574 : BL_PROFILE("FabArray::copy(fab)");
575 :
576 : BL_ASSERT(dcomp + ncomp <= dest.nComp());
577 : BL_ASSERT(IntVect(nghost).allLE(nGrowVect()));
578 :
579 : int root_proc = this->DistributionMap()[0];
580 :
581 : BoxArray ba(dest.box());
582 : DistributionMapping dm(Vector<int>{root_proc});
583 : FabArray<FAB> destmf(ba, dm, ncomp, 0, MFInfo().SetAlloc(false));
584 : if (ParallelDescriptor::MyProc() == root_proc) {
585 : destmf.setFab(0, FAB(dest, amrex::make_alias, dcomp, ncomp));
586 : }
587 :
588 : destmf.ParallelCopy(*this, scomp, 0, ncomp, nghost, 0);
589 :
590 : #ifdef BL_USE_MPI
591 : using T = typename FAB::value_type;
592 : if (ParallelContext::NProcsSub() > 1) {
593 : Long count = dest.numPts()*ncomp;
594 : T* const p0 = dest.dataPtr(dcomp);
595 : T* pb = p0;
596 : #ifdef AMREX_USE_GPU
597 : if (dest.arena()->isDevice()) {
598 : pb = (T*)The_Pinned_Arena()->alloc(sizeof(T)*count);
599 : Gpu::dtoh_memcpy_async(pb, p0, sizeof(T)*count);
600 : Gpu::streamSynchronize();
601 : }
602 : #endif
603 : ParallelDescriptor::Bcast(pb, count, ParallelContext::global_to_local_rank(root_proc),
604 : ParallelContext::CommunicatorSub());
605 : #ifdef AMREX_USE_GPU
606 : if (pb != p0) {
607 : Gpu::htod_memcpy_async(p0, pb, sizeof(T)*count);
608 : Gpu::streamSynchronize();
609 : }
610 : #endif
611 : }
612 : #endif
613 : }
614 : // \endcond
615 : #ifdef BL_USE_MPI
616 : template <class FAB>
617 : template <typename BUF>
618 : AMREX_NODISCARD TheFaArenaPointer
619 : FabArray<FAB>::PrepareSendBuffers (const MapOfCopyComTagContainers& SndTags,
620 : Vector<char*>& send_data,
621 : Vector<std::size_t>& send_size,
622 : Vector<int>& send_rank,
623 : Vector<MPI_Request>& send_reqs,
624 : Vector<const CopyComTagsContainer*>& send_cctc,
625 : int ncomp)
626 : {
627 : char* pointer = nullptr;
628 : PrepareSendBuffers<BUF>(SndTags, pointer, send_data, send_size, send_rank, send_reqs, send_cctc, ncomp);
629 : return TheFaArenaPointer(pointer);
630 : }
631 :
632 : template <class FAB>
633 : template <typename BUF>
634 : void
635 0 : FabArray<FAB>::PrepareSendBuffers (const MapOfCopyComTagContainers& SndTags,
636 : char*& the_send_data,
637 : Vector<char*>& send_data,
638 : Vector<std::size_t>& send_size,
639 : Vector<int>& send_rank,
640 : Vector<MPI_Request>& send_reqs,
641 : Vector<const CopyComTagsContainer*>& send_cctc,
642 : int ncomp)
643 : {
644 0 : send_data.clear();
645 0 : send_size.clear();
646 0 : send_rank.clear();
647 0 : send_reqs.clear();
648 0 : send_cctc.clear();
649 0 : const auto N_snds = SndTags.size();
650 0 : if (N_snds == 0) { return; }
651 0 : send_data.reserve(N_snds);
652 0 : send_size.reserve(N_snds);
653 0 : send_rank.reserve(N_snds);
654 0 : send_reqs.reserve(N_snds);
655 0 : send_cctc.reserve(N_snds);
656 :
657 0 : Vector<std::size_t> offset; offset.reserve(N_snds);
658 0 : std::size_t total_volume = 0;
659 0 : for (auto const& kv : SndTags)
660 : {
661 0 : auto const& cctc = kv.second;
662 :
663 0 : std::size_t nbytes = 0;
664 0 : for (auto const& cct : kv.second)
665 : {
666 0 : nbytes += cct.sbox.numPts() * ncomp * sizeof(BUF);
667 : }
668 :
669 0 : std::size_t acd = ParallelDescriptor::sizeof_selected_comm_data_type(nbytes);
670 0 : nbytes = amrex::aligned_size(acd, nbytes); // so that bytes are aligned
671 :
672 : // Also need to align the offset properly
673 0 : total_volume = amrex::aligned_size(std::max(alignof(BUF), acd),
674 : total_volume);
675 :
676 0 : offset.push_back(total_volume);
677 0 : total_volume += nbytes;
678 :
679 0 : send_data.push_back(nullptr);
680 0 : send_size.push_back(nbytes);
681 0 : send_rank.push_back(kv.first);
682 0 : send_reqs.push_back(MPI_REQUEST_NULL);
683 0 : send_cctc.push_back(&cctc);
684 : }
685 :
686 0 : if (total_volume > 0)
687 : {
688 0 : the_send_data = static_cast<char*>(amrex::The_Comms_Arena()->alloc(total_volume));
689 0 : for (int i = 0, N = static_cast<int>(send_size.size()); i < N; ++i) {
690 0 : send_data[i] = the_send_data + offset[i];
691 : }
692 : } else {
693 0 : the_send_data = nullptr;
694 : }
695 : }
696 :
697 : template <class FAB>
698 : void
699 0 : FabArray<FAB>::PostSnds (Vector<char*> const& send_data,
700 : Vector<std::size_t> const& send_size,
701 : Vector<int> const& send_rank,
702 : Vector<MPI_Request>& send_reqs,
703 : int SeqNum)
704 : {
705 0 : MPI_Comm comm = ParallelContext::CommunicatorSub();
706 :
707 0 : const auto N_snds = static_cast<int>(send_reqs.size());
708 0 : for (int j = 0; j < N_snds; ++j)
709 : {
710 0 : if (send_size[j] > 0) {
711 0 : const int rank = ParallelContext::global_to_local_rank(send_rank[j]);
712 0 : send_reqs[j] = ParallelDescriptor::Asend
713 0 : (send_data[j], send_size[j], rank, SeqNum, comm).req();
714 : }
715 : }
716 0 : }
717 :
718 : template <class FAB>
719 : template <typename BUF>
720 : TheFaArenaPointer FabArray<FAB>::PostRcvs (const MapOfCopyComTagContainers& RcvTags,
721 : Vector<char*>& recv_data,
722 : Vector<std::size_t>& recv_size,
723 : Vector<int>& recv_from,
724 : Vector<MPI_Request>& recv_reqs,
725 : int ncomp,
726 : int SeqNum)
727 : {
728 : char* pointer = nullptr;
729 : PostRcvs(RcvTags, pointer, recv_data, recv_size, recv_from, recv_reqs, ncomp, SeqNum);
730 : return TheFaArenaPointer(pointer);
731 : }
732 :
733 : template <class FAB>
734 : template <typename BUF>
735 : void
736 0 : FabArray<FAB>::PostRcvs (const MapOfCopyComTagContainers& RcvTags,
737 : char*& the_recv_data,
738 : Vector<char*>& recv_data,
739 : Vector<std::size_t>& recv_size,
740 : Vector<int>& recv_from,
741 : Vector<MPI_Request>& recv_reqs,
742 : int ncomp,
743 : int SeqNum)
744 : {
745 0 : recv_data.clear();
746 0 : recv_size.clear();
747 0 : recv_from.clear();
748 0 : recv_reqs.clear();
749 :
750 0 : Vector<std::size_t> offset;
751 0 : std::size_t TotalRcvsVolume = 0;
752 0 : for (const auto& kv : RcvTags) // loop over senders
753 : {
754 0 : std::size_t nbytes = 0;
755 0 : for (auto const& cct : kv.second)
756 : {
757 0 : nbytes += cct.dbox.numPts() * ncomp * sizeof(BUF);
758 : }
759 :
760 0 : std::size_t acd = ParallelDescriptor::sizeof_selected_comm_data_type(nbytes);
761 0 : nbytes = amrex::aligned_size(acd, nbytes); // so that nbytes are aligned
762 :
763 : // Also need to align the offset properly
764 0 : TotalRcvsVolume = amrex::aligned_size(std::max(alignof(BUF),acd),
765 : TotalRcvsVolume);
766 :
767 0 : offset.push_back(TotalRcvsVolume);
768 0 : TotalRcvsVolume += nbytes;
769 :
770 0 : recv_data.push_back(nullptr);
771 0 : recv_size.push_back(nbytes);
772 0 : recv_from.push_back(kv.first);
773 0 : recv_reqs.push_back(MPI_REQUEST_NULL);
774 : }
775 :
776 0 : const auto nrecv = static_cast<int>(recv_from.size());
777 :
778 0 : MPI_Comm comm = ParallelContext::CommunicatorSub();
779 :
780 0 : if (TotalRcvsVolume == 0)
781 : {
782 0 : the_recv_data = nullptr;
783 : }
784 : else
785 : {
786 0 : the_recv_data = static_cast<char*>(amrex::The_Comms_Arena()->alloc(TotalRcvsVolume));
787 :
788 0 : for (int i = 0; i < nrecv; ++i)
789 : {
790 0 : recv_data[i] = the_recv_data + offset[i];
791 0 : if (recv_size[i] > 0)
792 : {
793 0 : const int rank = ParallelContext::global_to_local_rank(recv_from[i]);
794 0 : recv_reqs[i] = ParallelDescriptor::Arecv
795 0 : (recv_data[i], recv_size[i], rank, SeqNum, comm).req();
796 : }
797 : }
798 : }
799 0 : }
800 : #endif
801 :
802 : template <class FAB>
803 : void
804 : FabArray<FAB>::Redistribute (const FabArray<FAB>& src,
805 : int scomp,
806 : int dcomp,
807 : int ncomp,
808 : const IntVect& nghost)
809 : {
810 : AMREX_ALWAYS_ASSERT_WITH_MESSAGE(boxArray() == src.boxArray(),
811 : "FabArray::Redistribute: must have the same BoxArray");
812 :
813 : if (ParallelContext::NProcsSub() == 1)
814 : {
815 : Copy(*this, src, scomp, dcomp, ncomp, nghost);
816 : return;
817 : }
818 :
819 : #ifdef BL_USE_MPI
820 :
821 : FabArrayBase::CPC cpc(boxArray(), nghost, DistributionMap(), src.DistributionMap());
822 :
823 : ParallelCopy(src, scomp, dcomp, ncomp, nghost, nghost, Periodicity::NonPeriodic(),
824 : FabArrayBase::COPY, &cpc);
825 :
826 : #endif
827 : }
828 :
829 : template <class FAB>
830 : void
831 0 : FabArray<FAB>::FillBoundary_test ()
832 : {
833 : #if defined(AMREX_USE_MPI) && !defined(AMREX_DEBUG)
834 : // We only test if no DEBUG because in DEBUG we check the status later.
835 : // If Test is done here, the status check will fail.
836 : int flag;
837 0 : ParallelDescriptor::Test(fbd->recv_reqs, flag, fbd->recv_stat);
838 : #endif
839 0 : }
840 :
841 : namespace detail {
842 : template <class TagT>
843 : void fbv_copy (Vector<TagT> const& tags)
844 : {
845 : const int N = tags.size();
846 : if (N == 0) { return; }
847 : #ifdef AMREX_USE_GPU
848 : if (Gpu::inLaunchRegion()) {
849 : ParallelFor(tags, 1,
850 : [=] AMREX_GPU_DEVICE (int i, int j, int k, int, TagT const& tag) noexcept
851 : {
852 : const int ncomp = tag.dfab.nComp();
853 : for (int n = 0; n < ncomp; ++n) {
854 : tag.dfab(i,j,k,n) = tag.sfab(i+tag.offset.x,j+tag.offset.y,k+tag.offset.z,n);
855 : }
856 : });
857 : } else
858 : #endif
859 : {
860 : #ifdef AMREX_USE_OMP
861 : #pragma omp parallel for
862 : #endif
863 : for (int itag = 0; itag < N; ++itag) {
864 : auto const& tag = tags[itag];
865 : const int ncomp = tag.dfab.nComp();
866 : AMREX_LOOP_4D(tag.dbox, ncomp, i, j, k, n,
867 : {
868 : tag.dfab(i,j,k,n) = tag.sfab(i+tag.offset.x,j+tag.offset.y,k+tag.offset.z,n);
869 : });
870 : }
871 : }
872 : }
873 : }
874 :
875 : template <class MF>
876 : std::enable_if_t<IsFabArray<MF>::value>
877 : FillBoundary (Vector<MF*> const& mf, Vector<int> const& scomp,
878 : Vector<int> const& ncomp, Vector<IntVect> const& nghost,
879 : Vector<Periodicity> const& period, Vector<int> const& cross = {})
880 : {
881 : BL_PROFILE("FillBoundary(Vector)");
882 : #if 1
883 : const int N = mf.size();
884 : for (int i = 0; i < N; ++i) {
885 : mf[i]->FillBoundary_nowait(scomp[i], ncomp[i], nghost[i], period[i],
886 : cross.empty() ? 0 : cross[i]);
887 : }
888 : for (int i = 0; i < N; ++i) {
889 : mf[i]->FillBoundary_finish();
890 : }
891 :
892 : #else
893 : using FAB = typename MF::FABType::value_type;
894 : using T = typename FAB::value_type;
895 :
896 : const int nmfs = mf.size();
897 : Vector<FabArrayBase::CommMetaData const*> cmds;
898 : int N_locs = 0;
899 : int N_rcvs = 0;
900 : int N_snds = 0;
901 : for (int imf = 0; imf < nmfs; ++imf) {
902 : if (nghost[imf].max() > 0) {
903 : auto const& TheFB = mf[imf]->getFB(nghost[imf], period[imf],
904 : cross.empty() ? 0 : cross[imf]);
905 : // The FB is cached. Therefore it's safe take its address for later use.
906 : cmds.push_back(static_cast<FabArrayBase::CommMetaData const*>(&TheFB));
907 : N_locs += TheFB.m_LocTags->size();
908 : N_rcvs += TheFB.m_RcvTags->size();
909 : N_snds += TheFB.m_SndTags->size();
910 : } else {
911 : cmds.push_back(nullptr);
912 : }
913 : }
914 :
915 : using TagT = Array4CopyTag<T>;
916 : Vector<TagT> local_tags;
917 : local_tags.reserve(N_locs);
918 : static_assert(amrex::IsStoreAtomic<T>::value, "FillBoundary(Vector): storing T is not atomic");
919 : for (int imf = 0; imf < nmfs; ++imf) {
920 : if (cmds[imf]) {
921 : auto const& tags = *(cmds[imf]->m_LocTags);
922 : for (auto const& tag : tags) {
923 : local_tags.push_back({(*mf[imf])[tag.dstIndex].array (scomp[imf],ncomp[imf]),
924 : (*mf[imf])[tag.srcIndex].const_array(scomp[imf],ncomp[imf]),
925 : tag.dbox,
926 : (tag.sbox.smallEnd()-tag.dbox.smallEnd()).dim3()});
927 : }
928 : }
929 : }
930 :
931 : if (ParallelContext::NProcsSub() == 1) {
932 : detail::fbv_copy(local_tags);
933 : return;
934 : }
935 :
936 : #ifdef AMREX_USE_MPI
937 : //
938 : // Do this before prematurely exiting if running in parallel.
939 : // Otherwise sequence numbers will not match across MPI processes.
940 : //
941 : int SeqNum = ParallelDescriptor::SeqNum();
942 : MPI_Comm comm = ParallelContext::CommunicatorSub();
943 :
944 : if (N_locs == 0 && N_rcvs == 0 && N_snds == 0) { return; } // No work to do
945 :
946 : char* the_recv_data = nullptr;
947 : Vector<int> recv_from;
948 : Vector<std::size_t> recv_size;
949 : Vector<MPI_Request> recv_reqs;
950 : Vector<MPI_Status> recv_stat;
951 : Vector<TagT> recv_tags;
952 :
953 : if (N_rcvs > 0) {
954 :
955 : for (int imf = 0; imf < nmfs; ++imf) {
956 : if (cmds[imf]) {
957 : auto const& tags = *(cmds[imf]->m_RcvTags);
958 : for (const auto& kv : tags) {
959 : recv_from.push_back(kv.first);
960 : }
961 : }
962 : }
963 : amrex::RemoveDuplicates(recv_from);
964 : const int nrecv = recv_from.size();
965 :
966 : recv_reqs.resize(nrecv, MPI_REQUEST_NULL);
967 : recv_stat.resize(nrecv);
968 :
969 : recv_tags.reserve(N_rcvs);
970 :
971 : Vector<Vector<std::size_t> > recv_offset(nrecv);
972 : Vector<std::size_t> offset;
973 : recv_size.reserve(nrecv);
974 : offset.reserve(nrecv);
975 : std::size_t TotalRcvsVolume = 0;
976 : for (int i = 0; i < nrecv; ++i) {
977 : std::size_t nbytes = 0;
978 : for (int imf = 0; imf < nmfs; ++imf) {
979 : if (cmds[imf]) {
980 : auto const& tags = *(cmds[imf]->m_RcvTags);
981 : auto it = tags.find(recv_from[i]);
982 : if (it != tags.end()) {
983 : for (auto const& cct : it->second) {
984 : auto& dfab = (*mf[imf])[cct.dstIndex];
985 : recv_offset[i].push_back(nbytes);
986 : recv_tags.push_back({dfab.array(scomp[imf],ncomp[imf]),
987 : makeArray4<T const>(nullptr,cct.dbox,ncomp[imf]),
988 : cct.dbox, Dim3{0,0,0}});
989 : nbytes += dfab.nBytes(cct.dbox,ncomp[imf]);
990 : }
991 : }
992 : }
993 : }
994 :
995 : std::size_t acd = ParallelDescriptor::sizeof_selected_comm_data_type(nbytes);
996 : nbytes = amrex::aligned_size(acd, nbytes); // so that nbytes are aligned
997 :
998 : // Also need to align the offset properly
999 : TotalRcvsVolume = amrex::aligned_size(std::max(alignof(T),acd), TotalRcvsVolume);
1000 :
1001 : offset.push_back(TotalRcvsVolume);
1002 : TotalRcvsVolume += nbytes;
1003 :
1004 : recv_size.push_back(nbytes);
1005 : }
1006 :
1007 : the_recv_data = static_cast<char*>(amrex::The_Comms_Arena()->alloc(TotalRcvsVolume));
1008 :
1009 : int k = 0;
1010 : for (int i = 0; i < nrecv; ++i) {
1011 : char* p = the_recv_data + offset[i];
1012 : const int rank = ParallelContext::global_to_local_rank(recv_from[i]);
1013 : recv_reqs[i] = ParallelDescriptor::Arecv
1014 : (p, recv_size[i], rank, SeqNum, comm).req();
1015 : for (int j = 0, nj = recv_offset[i].size(); j < nj; ++j) {
1016 : recv_tags[k++].sfab.p = (T const*)(p + recv_offset[i][j]);
1017 : }
1018 : }
1019 : }
1020 :
1021 : char* the_send_data = nullptr;
1022 : Vector<int> send_rank;
1023 : Vector<char*> send_data;
1024 : Vector<std::size_t> send_size;
1025 : Vector<MPI_Request> send_reqs;
1026 : if (N_snds > 0) {
1027 : for (int imf = 0; imf < nmfs; ++imf) {
1028 : if (cmds[imf]) {
1029 : auto const& tags = *(cmds[imf]->m_SndTags);
1030 : for (auto const& kv : tags) {
1031 : send_rank.push_back(kv.first);
1032 : }
1033 : }
1034 : }
1035 : amrex::RemoveDuplicates(send_rank);
1036 : const int nsend = send_rank.size();
1037 :
1038 : send_data.resize(nsend, nullptr);
1039 : send_reqs.resize(nsend, MPI_REQUEST_NULL);
1040 :
1041 : Vector<TagT> send_tags;
1042 : send_tags.reserve(N_snds);
1043 :
1044 : Vector<Vector<std::size_t> > send_offset(nsend);
1045 : Vector<std::size_t> offset;
1046 : send_size.reserve(nsend);
1047 : offset.reserve(nsend);
1048 : std::size_t TotalSndsVolume = 0;
1049 : for (int i = 0; i < nsend; ++i) {
1050 : std::size_t nbytes = 0;
1051 : for (int imf = 0; imf < nmfs; ++imf) {
1052 : if (cmds[imf]) {
1053 : auto const& tags = *(cmds[imf]->m_SndTags);
1054 : auto it = tags.find(send_rank[i]);
1055 : if (it != tags.end()) {
1056 : for (auto const& cct : it->second) {
1057 : auto const& sfab = (*mf[imf])[cct.srcIndex];
1058 : send_offset[i].push_back(nbytes);
1059 : send_tags.push_back({amrex::makeArray4<T>(nullptr,cct.sbox,ncomp[imf]),
1060 : sfab.const_array(scomp[imf],ncomp[imf]),
1061 : cct.sbox, Dim3{0,0,0}});
1062 : nbytes += sfab.nBytes(cct.sbox,ncomp[imf]);
1063 : }
1064 : }
1065 : }
1066 : }
1067 :
1068 : std::size_t acd = ParallelDescriptor::sizeof_selected_comm_data_type(nbytes);
1069 : nbytes = amrex::aligned_size(acd, nbytes); // so that bytes are aligned
1070 :
1071 : // Also need to align the offset properly
1072 : TotalSndsVolume = amrex::aligned_size(std::max(alignof(T),acd), TotalSndsVolume);
1073 :
1074 : offset.push_back(TotalSndsVolume);
1075 : TotalSndsVolume += nbytes;
1076 :
1077 : send_size.push_back(nbytes);
1078 : }
1079 :
1080 : the_send_data = static_cast<char*>(amrex::The_Comms_Arena()->alloc(TotalSndsVolume));
1081 : int k = 0;
1082 : for (int i = 0; i < nsend; ++i) {
1083 : send_data[i] = the_send_data + offset[i];
1084 : for (int j = 0, nj = send_offset[i].size(); j < nj; ++j) {
1085 : send_tags[k++].dfab.p = (T*)(send_data[i] + send_offset[i][j]);
1086 : }
1087 : }
1088 :
1089 : detail::fbv_copy(send_tags);
1090 :
1091 : FabArray<FAB>::PostSnds(send_data, send_size, send_rank, send_reqs, SeqNum);
1092 : }
1093 :
1094 : #if !defined(AMREX_DEBUG)
1095 : int recv_flag;
1096 : ParallelDescriptor::Test(recv_reqs, recv_flag, recv_stat);
1097 : #endif
1098 :
1099 : if (N_locs > 0) {
1100 : detail::fbv_copy(local_tags);
1101 : #if !defined(AMREX_DEBUG)
1102 : ParallelDescriptor::Test(recv_reqs, recv_flag, recv_stat);
1103 : #endif
1104 : }
1105 :
1106 : if (N_rcvs > 0) {
1107 : ParallelDescriptor::Waitall(recv_reqs, recv_stat);
1108 : #ifdef AMREX_DEBUG
1109 : if (!FabArrayBase::CheckRcvStats(recv_stat, recv_size, SeqNum)) {
1110 : amrex::Abort("FillBoundary(vector) failed with wrong message size");
1111 : }
1112 : #endif
1113 :
1114 : detail::fbv_copy(recv_tags);
1115 :
1116 : amrex::The_Comms_Arena()->free(the_recv_data);
1117 : }
1118 :
1119 : if (N_snds > 0) {
1120 : Vector<MPI_Status> stats(send_reqs.size());
1121 : ParallelDescriptor::Waitall(send_reqs, stats);
1122 : amrex::The_Comms_Arena()->free(the_send_data);
1123 : }
1124 :
1125 : #endif // #ifdef AMREX_USE_MPI
1126 : #endif // #if 1 #else
1127 : }
1128 :
1129 : template <class MF>
1130 : std::enable_if_t<IsFabArray<MF>::value>
1131 : FillBoundary (Vector<MF*> const& mf, const Periodicity& a_period = Periodicity::NonPeriodic())
1132 : {
1133 : Vector<int> scomp(mf.size(), 0);
1134 : Vector<int> ncomp;
1135 : Vector<IntVect> nghost;
1136 : Vector<Periodicity> period(mf.size(), a_period);
1137 : ncomp.reserve(mf.size());
1138 : nghost.reserve(mf.size());
1139 : for (auto const& x : mf) {
1140 : ncomp.push_back(x->nComp());
1141 : nghost.push_back(x->nGrowVect());
1142 : }
1143 : FillBoundary(mf, scomp, ncomp, nghost, period);
1144 : }
|