Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_Reduce.H
Go to the documentation of this file.
1#ifndef AMREX_REDUCE_H_
2#define AMREX_REDUCE_H_
3#include <AMReX_Config.H>
4
5#include <AMReX_Concepts.H>
6#include <AMReX_Gpu.H>
7#include <AMReX_Arena.H>
8#include <AMReX_OpenMP.H>
9#include <AMReX_MFIter.H>
10#include <AMReX_TypeList.H>
11#include <AMReX_ValLocPair.H>
12
13#include <algorithm>
14#include <concepts>
15#include <functional>
16#include <limits>
17
18namespace amrex {
19
20namespace Reduce {
21
22// The declaration of these functions are here to work around doxygen issues.
23
38template <typename T, std::integral N>
39T Sum (N n, T const* v, T init_val = 0);
40
57template <typename T, std::integral N, typename F>
58requires (!std::same_as<T*,std::decay_t<F>>)
59T Sum (N n, F const& f, T init_val = 0);
60
75template <typename T, std::integral N>
76T Min (N n, T const* v, T init_val = std::numeric_limits<T>::max());
77
94template <typename T, std::integral N, typename F>
95requires (!std::same_as<T*,std::decay_t<F>>)
96T Min (N n, F const& f, T init_val = std::numeric_limits<T>::max());
97
112template <typename T, std::integral N>
113T Max (N n, T const* v, T init_val = std::numeric_limits<T>::lowest());
114
131template <typename T, std::integral N, typename F>
132requires (!std::same_as<T*,std::decay_t<F>>)
133T Max (N n, F const& f, T init_val = std::numeric_limits<T>::lowest());
134
148template <typename T, std::integral N>
149std::pair<T,T> MinMax (N n, T const* v);
150
166template <typename T, std::integral N, typename F>
167requires (!std::same_as<T*,std::decay_t<F>>)
168std::pair<T,T> MinMax (N n, F const& f);
169
185template <typename T, std::integral N, typename P>
186bool AnyOf (N n, T const* v, P const& pred);
187
201template <typename P, int dim>
202bool AnyOf (BoxND<dim> const& box, P const& pred);
203
204}
205
207namespace Reduce::detail {
208
209#ifdef AMREX_USE_GPU
210#ifdef AMREX_USE_SYCL
211 template <std::size_t I, typename T, typename P>
213 void for_each_parallel (T& d, T const& s, Gpu::Handler const& h)
214 {
215 P().parallel_update(amrex::get<I>(d), amrex::get<I>(s), h);
216 }
217
218 template <std::size_t I, typename T, typename P, typename P1, typename... Ps>
220 void for_each_parallel (T& d, T const& s, Gpu::Handler const& h)
221 {
222 P().parallel_update(amrex::get<I>(d), amrex::get<I>(s), h);
223 for_each_parallel<I+1,T,P1,Ps...>(d, s, h);
224 }
225#else
226 template <std::size_t I, typename T, typename P>
228 void for_each_parallel (T& d, T const& s)
229 {
230 P().parallel_update(amrex::get<I>(d), amrex::get<I>(s));
231 }
232
233 template <std::size_t I, typename T, typename P, typename P1, typename... Ps>
235 void for_each_parallel (T& d, T const& s)
236 {
237 P().parallel_update(amrex::get<I>(d), amrex::get<I>(s));
238 for_each_parallel<I+1,T,P1,Ps...>(d, s);
239 }
240#endif
241#endif
242
243 template <std::size_t I, typename T, typename P>
245 void for_each_local (T& d, T const& s)
246 {
247 P().local_update(amrex::get<I>(d), amrex::get<I>(s));
248 }
249
250 template <std::size_t I, typename T, typename P, typename P1, typename... Ps>
252 void for_each_local (T& d, T const& s)
253 {
254 P().local_update(amrex::get<I>(d), amrex::get<I>(s));
255 for_each_local<I+1,T,P1,Ps...>(d, s);
256 }
257
258 template <std::size_t I, typename T, typename P>
260 constexpr void for_each_init (T& t)
261 {
262 P().init(amrex::get<I>(t));
263 }
264
265 template <std::size_t I, typename T, typename P, typename P1, typename... Ps>
267 constexpr void for_each_init (T& t)
268 {
269 P().init(amrex::get<I>(t));
270 for_each_init<I+1,T,P1,Ps...>(t);
271 }
272}
274
277{
278
279#ifdef AMREX_USE_GPU
280#ifdef AMREX_USE_SYCL
281 template <typename T>
283 void parallel_update (T& d, T const& s, Gpu::Handler const& h) const noexcept {
284 T r = Gpu::blockReduceSum(s,h);
285 if (h.threadIdx() == 0) { d += r; }
286 }
287#else
288 template <typename T, int MT=AMREX_GPU_MAX_THREADS>
290 void parallel_update (T& d, T const& s) const noexcept {
291 T r = Gpu::blockReduceSum<MT>(s);
292 if (threadIdx.x == 0) { d += r; }
293 }
294#endif
295#endif
296
297 template <typename T>
299 void local_update (T& d, T const& s) const noexcept { d += s; }
300
301 template <typename T>
302 constexpr void init (T& t) const noexcept { t = 0; }
303};
304
307{
308#ifdef AMREX_USE_GPU
309#ifdef AMREX_USE_SYCL
310 template <typename T>
312 void parallel_update (T& d, T const& s, Gpu::Handler const& h) const noexcept {
313 T r = Gpu::blockReduceMin(s,h);
314 if (h.threadIdx() == 0) { d = amrex::min(d,r); }
315 }
316#else
317 template <typename T, int MT=AMREX_GPU_MAX_THREADS>
319 void parallel_update (T& d, T const& s) const noexcept {
320 T r = Gpu::blockReduceMin<MT>(s);
321 if (threadIdx.x == 0) { d = amrex::min(d,r); }
322 }
323#endif
324#endif
325
326 template <typename T>
328 void local_update (T& d, T const& s) const noexcept { d = amrex::min(d,s); }
329
330 template <typename T>
331 requires (std::numeric_limits<T>::is_specialized)
332 constexpr void init (T& t) const noexcept { t = std::numeric_limits<T>::max(); }
333
334 template <typename T>
335 requires (!std::numeric_limits<T>::is_specialized)
336 constexpr void init (T& t) const noexcept { t = T::max(); }
337};
338
341{
342#ifdef AMREX_USE_GPU
343#ifdef AMREX_USE_SYCL
344 template <typename T>
346 void parallel_update (T& d, T const& s, Gpu::Handler const& h) const noexcept {
347 T r = Gpu::blockReduceMax(s,h);
348 if (h.threadIdx() == 0) { d = amrex::max(d,r); }
349 }
350#else
351 template <typename T, int MT=AMREX_GPU_MAX_THREADS>
353 void parallel_update (T& d, T const& s) const noexcept {
354 T r = Gpu::blockReduceMax<MT>(s);
355 if (threadIdx.x == 0) { d = amrex::max(d,r); }
356 }
357#endif
358#endif
359
360 template <typename T>
362 void local_update (T& d, T const& s) const noexcept { d = amrex::max(d,s); }
363
364 template <typename T>
365 requires (std::numeric_limits<T>::is_specialized)
366 constexpr void init (T& t) const noexcept { t = std::numeric_limits<T>::lowest(); }
367
368 template <typename T>
369 requires (!std::numeric_limits<T>::is_specialized)
370 constexpr void init (T& t) const noexcept { t = T::lowest(); }
371};
372
375{
376#ifdef AMREX_USE_GPU
377#ifdef AMREX_USE_SYCL
378 template <std::integral T>
380 void parallel_update (T& d, T s, Gpu::Handler const& h) const noexcept {
382 if (h.threadIdx() == 0) { d = d && r; }
383 }
384#else
385 template <std::integral T, int MT=AMREX_GPU_MAX_THREADS>
387 void parallel_update (T& d, T s) const noexcept {
388 T r = Gpu::blockReduceLogicalAnd<MT>(s);
389 if (threadIdx.x == 0) { d = d && r; }
390 }
391#endif
392#endif
393
394 template <std::integral T>
396 void local_update (T& d, T s) const noexcept { d = d && s; }
397
398 template <std::integral T>
399 constexpr void init (T& t) const noexcept { t = true; }
400};
401
404{
405#ifdef AMREX_USE_GPU
406#ifdef AMREX_USE_SYCL
407 template <std::integral T>
409 void parallel_update (T& d, T s, Gpu::Handler const& h) const noexcept {
410 T r = Gpu::blockReduceLogicalOr(s,h);
411 if (h.threadIdx() == 0) { d = d || r; }
412 }
413#else
414 template <std::integral T, int MT=AMREX_GPU_MAX_THREADS>
416 void parallel_update (T& d, T s) const noexcept {
417 T r = Gpu::blockReduceLogicalOr<MT>(s);
418 if (threadIdx.x == 0) { d = d || r; }
419 }
420#endif
421#endif
422
423 template <std::integral T>
425 void local_update (T& d, T s) const noexcept { d = d || s; }
426
427 template <std::integral T>
428 constexpr void init (T& t) const noexcept { t = false; }
429};
430
431template <typename... Ps> class ReduceOps;
432
433#ifdef AMREX_USE_GPU
434
436template <typename... Ts>
438{
439public:
440 using Type = GpuTuple<Ts...>;
441
442 template <typename... Ps>
443 explicit ReduceData (ReduceOps<Ps...>& reduce_op)
444 : m_max_blocks(Gpu::Device::maxBlocksPerLaunch()),
445 m_host_tuple((Type*)(The_Pinned_Arena()->alloc(sizeof(Type)))),
446 m_device_tuple((Type*)(The_Arena()->alloc((AMREX_GPU_MAX_STREAMS)
447 * m_max_blocks * sizeof(Type)))),
448 m_fn_value([&reduce_op,this] () -> Type { return this->value(reduce_op); })
449 {
450 reduce_op.resetResultReadiness();
451 static_assert(std::is_trivially_copyable<Type>(),
452 "ReduceData::Type must be trivially copyable");
453 static_assert(std::is_trivially_destructible<Type>(),
454 "ReduceData::Type must be trivially destructible");
455
456 new (m_host_tuple) Type();
457 m_nblocks.fill(0);
458 }
459
462 !m_used_external_stream || m_value_called,
463 "ReduceData used on an external GPU stream must call value() before destruction.");
464 The_Pinned_Arena()->free(m_host_tuple);
465 The_Arena()->free(m_device_tuple);
466 }
467
468 ReduceData (ReduceData<Ts...> const&) = delete;
470 void operator= (ReduceData<Ts...> const&) = delete;
471 void operator= (ReduceData<Ts...> &&) = delete;
472
474 {
475 Type r = m_fn_value();
476 m_value_called = true;
477 return r;
478 }
479
480 template <typename... Ps>
482 {
483 Type r = reduce_op.value(*this);
484 m_value_called = true;
485 return r;
486 }
487
488 Type* devicePtr () { return m_device_tuple; }
490 return m_device_tuple+streamIndexChecked(s)*m_max_blocks;
491 }
492
493 Type* hostPtr () { return m_host_tuple; }
494
496 int& nBlocks (gpuStream_t const& s) { return m_nblocks[streamIndexChecked(s)]; }
497
498 int maxBlocks () const { return m_max_blocks; }
499
500 int maxStreamIndex () const { return m_max_stream_index; }
502 m_max_stream_index = std::max(m_max_stream_index,streamIndexChecked(s));
503 }
504
505 void markValueCalled () noexcept { m_value_called = true; }
506
507private:
508 int streamIndexChecked (gpuStream_t const& s)
509 {
510 int const idx = Gpu::Device::streamIndex(s);
511 m_used_external_stream = m_used_external_stream || Gpu::Device::usingExternalStream();
512 if (idx == 0) {
513 if (m_stream_index_zero_set) {
514 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_stream_index_zero == s,
515 "ReduceData cannot be reused across different external GPU streams "
516 "or between an external GPU stream and AMReX stream 0.");
517 } else {
518 m_stream_index_zero = s;
519 m_stream_index_zero_set = true;
520 }
521 }
522 return idx;
523 }
524
525 int m_max_blocks;
526 int m_max_stream_index = 0;
527 Type* m_host_tuple = nullptr;
528 Type* m_device_tuple = nullptr;
529 GpuArray<int,AMREX_GPU_MAX_STREAMS> m_nblocks;
530 gpuStream_t m_stream_index_zero{};
531 bool m_stream_index_zero_set = false;
532 bool m_used_external_stream = false;
533 bool m_value_called = false;
534 std::function<Type()> m_fn_value;
535};
536
538namespace Reduce::detail {
539
540 // call_f_intvect_box
541
542 template <typename F, int dim>
544 auto call_f_intvect_box (F const& f, IntVectND<dim> iv, IndexTypeND<dim>) noexcept ->
545 decltype(amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv))
546 {
547 return amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv);
548 }
549
550 template <typename F, int dim>
552 auto call_f_intvect_box (F const& f, IntVectND<dim> iv, IndexTypeND<dim> t) noexcept ->
553 decltype(f(BoxND<dim>(iv, iv, t)))
554 {
555 return f(BoxND<dim>(iv, iv, t));
556 }
557
558 // call_f_intvect_n
559 template <typename F, typename T, int dim>
561 auto call_f_intvect_n (F const& f, IntVectND<dim> iv, T n) noexcept ->
562 decltype(amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv, n))
563 {
564 return amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv, n);
565 }
566
567 // mf_call_f
568
569 struct iterate_box {};
570 struct iterate_box_comp {};
571
572 template <typename I, typename F, typename T, typename... Ps>
573 requires (std::same_as<iterate_box,I>)
575 void mf_call_f (F const& f, int ibox, int i, int j, int k, int, T& r) noexcept
576 {
577 auto const& pr = f(ibox,i,j,k);
578 Reduce::detail::for_each_local<0, T, Ps...>(r, pr);
579 }
580
581 template <typename I, typename F, typename T, typename... Ps>
582 requires (std::same_as<iterate_box_comp,I>)
584 void mf_call_f (F const& f, int ibox, int i, int j, int k, int ncomp, T& r) noexcept
585 {
586 for (int n = 0; n < ncomp; ++n) {
587 auto const& pr = f(ibox,i,j,k,n);
588 Reduce::detail::for_each_local<0, T, Ps...>(r, pr);
589 }
590 }
591}
593
595template <typename... Ps>
597{
598public:
599
601
602 // This is public for CUDA
603 template <typename I, typename MF, typename D, typename F>
604 void eval_mf (I, MF const& mf, IntVect const& nghost, int ncomp, D& reduce_data, F const& f)
605 {
606 using ReduceTuple = typename D::Type;
607 const int nboxes = mf.local_size();
608 if (nboxes > 0) {
609 auto const& parforinfo = mf.getParForInfo(nghost);
610 auto nblocks_per_box = parforinfo.getNBlocksPerBox(AMREX_GPU_MAX_THREADS);
611 AMREX_ASSERT(Long(nblocks_per_box)*Long(nboxes) < Long(std::numeric_limits<int>::max()));
612 const int nblocks = nblocks_per_box * nboxes;
613 const BoxIndexer* dp_boxes = parforinfo.getBoxes();
614
615 auto const& stream = Gpu::gpuStream();
616 auto pdst = reduce_data.devicePtr(stream);
617 int nblocks_ec = std::min(nblocks, reduce_data.maxBlocks());
618 AMREX_ASSERT(Long(nblocks_ec)*2 <= Long(std::numeric_limits<int>::max()));
619 int& nblocks_ref = reduce_data.nBlocks(stream);
620 auto old_nblocks = static_cast<unsigned int>(nblocks_ref);
621 nblocks_ref = amrex::max(nblocks_ref, nblocks_ec);
622 reduce_data.updateMaxStreamIndex(stream);
623
624#ifdef AMREX_USE_SYCL
625 // device reduce needs local(i.e., shared) memory
626 constexpr std::size_t shared_mem_bytes = sizeof(ReduceTuple)
627 * std::max(Gpu::Device::warp_size, AMREX_GPU_MAX_THREADS/Gpu::Device::warp_size);
628 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, shared_mem_bytes, stream,
629 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
630 {
631 Dim1 blockIdx {gh.blockIdx()};
632 Dim1 threadIdx{gh.threadIdx()};
633#else
634 amrex::launch_global<AMREX_GPU_MAX_THREADS>
635 <<<nblocks_ec, AMREX_GPU_MAX_THREADS, 0, stream>>>
636 ([=] AMREX_GPU_DEVICE () noexcept
637 {
638#endif
639 ReduceTuple r;
640 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
641 ReduceTuple& dst = pdst[blockIdx.x];
642 if (threadIdx.x == 0 && blockIdx.x >= old_nblocks) {
643 dst = r;
644 }
645 for (int iblock = blockIdx.x; iblock < nblocks; iblock += nblocks_ec) {
646 int ibox = iblock / nblocks_per_box;
647 auto icell = std::uint64_t(iblock-ibox*nblocks_per_box)*AMREX_GPU_MAX_THREADS + threadIdx.x;
648
649 BoxIndexer const& indexer = dp_boxes[ibox];
650 if (icell < indexer.numPts()) {
651 auto [i, j, k] = indexer(icell);
652 Reduce::detail::mf_call_f<I, F, ReduceTuple, Ps...>
653 (f, ibox, i, j, k, ncomp, r);
654 }
655 }
656#ifdef AMREX_USE_SYCL
657 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r, gh);
658#else
659 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r);
660#endif
661 });
662 }
663 }
664
665 // This is public for CUDA
666 template <typename I, int dim, typename D, typename F>
667 void eval_box (I, BoxND<dim> const& box, int ncomp, D& reduce_data, F const& f)
668 {
669 if (box.isEmpty()) { return; }
670 using ReduceTuple = typename D::Type;
671 auto const& stream = Gpu::gpuStream();
672 auto dp = reduce_data.devicePtr(stream);
673 int& nblocks = reduce_data.nBlocks(stream);
674 const BoxIndexerND<dim> indexer(box);
675 IndexTypeND<dim> ixtype = box.ixType();
676 constexpr int nitems_per_thread = 4;
677 Long nblocks_ec = (box.numPts() + nitems_per_thread*AMREX_GPU_MAX_THREADS-1)
678 / (nitems_per_thread*AMREX_GPU_MAX_THREADS);
679 nblocks_ec = std::min<Long>(nblocks_ec, reduce_data.maxBlocks());
680 reduce_data.updateMaxStreamIndex(stream);
681#ifdef AMREX_USE_SYCL
682 // device reduce needs local(i.e., shared) memory
683 constexpr std::size_t shared_mem_bytes = sizeof(ReduceTuple)
684 * std::max(Gpu::Device::warp_size, AMREX_GPU_MAX_THREADS/Gpu::Device::warp_size);
685 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, shared_mem_bytes, stream,
686 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
687 {
688 Dim1 blockIdx {gh.blockIdx()};
689 Dim1 threadIdx{gh.threadIdx()};
690 Dim1 gridDim {gh.gridDim()};
691#else
692 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, 0, stream,
693 [=] AMREX_GPU_DEVICE () noexcept
694 {
695#endif
696 ReduceTuple r;
697 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
698 ReduceTuple& dst = *(dp+blockIdx.x);
699 if (threadIdx.x == 0 && static_cast<int>(blockIdx.x) >= nblocks) {
700 dst = r;
701 }
702 for (std::uint64_t icell = std::uint64_t(AMREX_GPU_MAX_THREADS)*blockIdx.x+threadIdx.x,
703 stride = std::uint64_t(AMREX_GPU_MAX_THREADS)*gridDim.x;
704 icell < indexer.numPts();
705 icell += stride)
706 {
707 auto iv = indexer.intVect(icell);
708 amrex::ignore_unused(f,ncomp,ixtype); // work around first-capture
709 if constexpr (std::is_same_v<Reduce::detail::iterate_box,I>) {
710 auto pr = Reduce::detail::call_f_intvect_box(f, iv, ixtype);
711 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, pr);
712 } else {
713 for (int n = 0; n < ncomp; ++n) {
714 auto pr = Reduce::detail::call_f_intvect_n(f, iv, n);
715 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, pr);
716 }
717 }
718 }
719#ifdef AMREX_USE_SYCL
720 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r, gh);
721#else
722 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r);
723#endif
724 });
725 nblocks = std::max(nblocks, static_cast<int>(nblocks_ec));
726 }
727
729
730 template <FabArrayType MF, typename D, typename F>
731#ifndef AMREX_USE_CUDA
733#endif
734 void eval (MF const& mf, IntVect const& nghost, D& reduce_data, F&& f)
735 {
736 using ReduceTuple = typename D::Type;
737 const int nboxes = mf.local_size();
738 if (nboxes == 0) {
739 return;
740 } else if (!mf.isFusingCandidate()) {
741 for (MFIter mfi(mf); mfi.isValid(); ++mfi) {
742 Box const& b = amrex::grow(mfi.validbox(), nghost);
743 const int li = mfi.LocalIndex();
744 this->eval(b, reduce_data,
745 [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept -> ReduceTuple
746 {
747 return f(li, i, j, k);
748 });
749 }
750 } else {
751 eval_mf(Reduce::detail::iterate_box{},
752 mf, nghost, 0, reduce_data, std::forward<F>(f));
753 }
754 }
755
756 template <FabArrayType MF, typename D, typename F>
757#ifndef AMREX_USE_CUDA
759#endif
760 void eval (MF const& mf, IntVect const& nghost, int ncomp, D& reduce_data, F&& f)
761 {
762 using ReduceTuple = typename D::Type;
763
764 const int nboxes = mf.local_size();
765
766 if (nboxes == 0) {
767 return;
768 } else if (!mf.isFusingCandidate()) {
769 for (MFIter mfi(mf); mfi.isValid(); ++mfi) {
770 Box const& b = amrex::grow(mfi.validbox(), nghost);
771 const int li = mfi.LocalIndex();
772 this->eval(b, ncomp, reduce_data,
773 [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept -> ReduceTuple
774 {
775 return f(li, i, j, k, n);
776 });
777 }
778 } else {
779 eval_mf(Reduce::detail::iterate_box_comp{},
780 mf, nghost, ncomp, reduce_data, std::forward<F>(f));
781 }
782 }
783
784 template <typename D, typename F, int dim>
785 void eval (BoxND<dim> const& box, D & reduce_data, F const& f)
786 {
787 eval_box(Reduce::detail::iterate_box{}, box, 0, reduce_data, f);
788 }
789
790 template <std::integral N, typename D, typename F, int dim>
791 void eval (BoxND<dim> const& box, N ncomp, D & reduce_data, F const& f)
792 {
793 eval_box(Reduce::detail::iterate_box_comp{}, box, ncomp, reduce_data, f);
794 }
795
796 template <std::integral N, typename D, typename F>
797 void eval (N n, D & reduce_data, F const& f)
798 {
799 if (n <= 0) { return; }
800 using ReduceTuple = typename D::Type;
801 auto const& stream = Gpu::gpuStream();
802 auto dp = reduce_data.devicePtr(stream);
803 int& nblocks = reduce_data.nBlocks(stream);
804 constexpr int nitems_per_thread = 4;
805 int nblocks_ec = (n + nitems_per_thread*AMREX_GPU_MAX_THREADS-1)
806 / (nitems_per_thread*AMREX_GPU_MAX_THREADS);
807 nblocks_ec = std::min(nblocks_ec, reduce_data.maxBlocks());
808 reduce_data.updateMaxStreamIndex(stream);
809#ifdef AMREX_USE_SYCL
810 // device reduce needs local(i.e., shared) memory
811 constexpr std::size_t shared_mem_bytes = sizeof(ReduceTuple)
812 * std::max(Gpu::Device::warp_size, AMREX_GPU_MAX_THREADS/Gpu::Device::warp_size);
813 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, shared_mem_bytes, stream,
814 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
815 {
816 Dim1 blockIdx {gh.blockIdx()};
817 Dim1 threadIdx{gh.threadIdx()};
818 Dim1 gridDim {gh.gridDim()};
819#else
820 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, 0, stream,
821 [=] AMREX_GPU_DEVICE () noexcept
822 {
823#endif
824 ReduceTuple r;
825 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
826 ReduceTuple& dst = *(dp+blockIdx.x);
827 if (threadIdx.x == 0 && static_cast<int>(blockIdx.x) >= nblocks) {
828 dst = r;
829 }
830 for (N i = N(AMREX_GPU_MAX_THREADS)*blockIdx.x+threadIdx.x,
831 stride = N(AMREX_GPU_MAX_THREADS)*gridDim.x;
832 i < n;
833 i += stride)
834 {
835 auto pr = f(i);
836 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r,pr);
837 }
838#ifdef AMREX_USE_SYCL
839 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r, gh);
840#else
841 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r);
842#endif
843 });
844 nblocks = amrex::max(nblocks, nblocks_ec);
845 }
846
847 template <typename D>
848 typename D::Type value (D & reduce_data)
849 {
850 auto hp = reduce_data.hostPtr();
851
852 if (m_result_is_ready) {
853 reduce_data.markValueCalled();
854 return *hp;
855 }
856
857 using ReduceTuple = typename D::Type;
858 auto const& stream = Gpu::gpuStream();
859 auto dp = reduce_data.devicePtr();
860 auto const& nblocks = reduce_data.nBlocks();
861#if defined(AMREX_USE_SYCL)
862 if (reduce_data.maxStreamIndex() == 0 && nblocks[0] <= 4096) {
863 const int N = nblocks[0];
864 if (N == 0) {
865 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(*hp);
866 } else {
868 Gpu::dtoh_memcpy_async(tmp.data(), dp, sizeof(ReduceTuple)*N);
869 Gpu::streamSynchronize();
870 for (int i = 1; i < N; ++i) {
871 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(tmp[0], tmp[i]);
872 }
873 *hp = tmp[0];
874 }
875 } else
876#endif
877 {
878 int maxblocks = reduce_data.maxBlocks();
879#ifdef AMREX_USE_SYCL
880 // device reduce needs local(i.e., shared) memory
881 constexpr std::size_t shared_mem_bytes = sizeof(ReduceTuple)
882 * std::max(Gpu::Device::warp_size, AMREX_GPU_MAX_THREADS/Gpu::Device::warp_size);
883#ifndef AMREX_NO_SYCL_REDUCE_WORKAROUND
884 // xxxxx SYCL todo: reduce bug workaround
886 auto presult = dtmp.data();
887#else
888 auto presult = hp;
889#endif
890 amrex::launch<AMREX_GPU_MAX_THREADS>(1, shared_mem_bytes, stream,
891 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
892 {
893 ReduceTuple r;
894 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
895 ReduceTuple dst = r;
896 for (int istream = 0, nstreams = nblocks.size(); istream < nstreams; ++istream) {
897 auto dp_stream = dp+istream*maxblocks;
898 for (int i = gh.item->get_global_id(0), stride = gh.item->get_global_range(0);
899 i < nblocks[istream]; i += stride) {
900 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, dp_stream[i]);
901 }
902 }
903 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r, gh);
904 if (gh.threadIdx() == 0) { *presult = dst; }
905 });
906#ifndef AMREX_NO_SYCL_REDUCE_WORKAROUND
907 Gpu::dtoh_memcpy_async(hp, dtmp.data(), sizeof(ReduceTuple));
908#endif
909#else
910 amrex::launch<AMREX_GPU_MAX_THREADS>(1, 0, stream,
911 [=] AMREX_GPU_DEVICE () noexcept
912 {
913 ReduceTuple r;
914 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
915 ReduceTuple dst = r;
916 for (int istream = 0, nstreams = nblocks.size(); istream < nstreams; ++istream) {
917 auto dp_stream = dp+istream*maxblocks;
918 for (int i = AMREX_GPU_MAX_THREADS*blockIdx.x+threadIdx.x, stride = AMREX_GPU_MAX_THREADS*gridDim.x;
919 i < nblocks[istream]; i += stride) {
920 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, dp_stream[i]);
921 }
922 }
923 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r);
924 if (threadIdx.x == 0) { *hp = dst; }
925 });
926#endif
927 Gpu::streamSynchronize();
928 }
929
930 m_result_is_ready = true;
931 reduce_data.markValueCalled();
932 return *hp;
933 }
934
935private:
936 template <typename... T> friend class ReduceData;
937 bool m_result_is_ready = false;
938 void resetResultReadiness () { m_result_is_ready = false; }
939};
940
941namespace Reduce {
942
943template <typename T, std::integral N>
944T Sum (N n, T const* v, T init_val)
945{
946 ReduceOps<ReduceOpSum> reduce_op;
947 ReduceData<T> reduce_data(reduce_op);
948 using ReduceTuple = typename decltype(reduce_data)::Type;
949 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {v[i]}; });
950 ReduceTuple hv = reduce_data.value(reduce_op);
951 return amrex::get<0>(hv) + init_val;
952}
953
954template <typename T, std::integral N, typename F>
955requires (!std::same_as<T*,std::decay_t<F>>)
956T Sum (N n, F const& f, T init_val)
957{
958 ReduceOps<ReduceOpSum> reduce_op;
959 ReduceData<T> reduce_data(reduce_op);
960 using ReduceTuple = typename decltype(reduce_data)::Type;
961 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {f(i)}; });
962 ReduceTuple hv = reduce_data.value(reduce_op);
963 return amrex::get<0>(hv) + init_val;
964}
965
966template <typename T, std::integral N>
967T Min (N n, T const* v, T init_val)
968{
969 ReduceOps<ReduceOpMin> reduce_op;
970 ReduceData<T> reduce_data(reduce_op);
971 using ReduceTuple = typename decltype(reduce_data)::Type;
972 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {v[i]}; });
973 ReduceTuple hv = reduce_data.value(reduce_op);
974 return std::min(amrex::get<0>(hv),init_val);
975}
976
977template <typename T, std::integral N, typename F>
978requires (!std::same_as<T*,std::decay_t<F>>)
979T Min (N n, F const& f, T init_val)
980{
981 ReduceOps<ReduceOpMin> reduce_op;
982 ReduceData<T> reduce_data(reduce_op);
983 using ReduceTuple = typename decltype(reduce_data)::Type;
984 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {f(i)}; });
985 ReduceTuple hv = reduce_data.value(reduce_op);
986 return std::min(amrex::get<0>(hv),init_val);
987}
988
989template <typename T, std::integral N>
990T Max (N n, T const* v, T init_val)
991{
992 ReduceOps<ReduceOpMax> reduce_op;
993 ReduceData<T> reduce_data(reduce_op);
994 using ReduceTuple = typename decltype(reduce_data)::Type;
995 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {v[i]}; });
996 ReduceTuple hv = reduce_data.value(reduce_op);
997 return std::max(amrex::get<0>(hv),init_val);
998}
999
1000template <typename T, std::integral N, typename F>
1001requires (!std::same_as<T*,std::decay_t<F>>)
1002T Max (N n, F const& f, T init_val)
1003{
1004 ReduceOps<ReduceOpMax> reduce_op;
1005 ReduceData<T> reduce_data(reduce_op);
1006 using ReduceTuple = typename decltype(reduce_data)::Type;
1007 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {f(i)}; });
1008 ReduceTuple hv = reduce_data.value(reduce_op);
1009 return std::max(amrex::get<0>(hv),init_val);
1010}
1011
1012template <typename T, std::integral N>
1013std::pair<T,T> MinMax (N n, T const* v)
1014{
1016 ReduceData<T,T> reduce_data(reduce_op);
1017 using ReduceTuple = typename decltype(reduce_data)::Type;
1018 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple {
1019 return {v[i],v[i]};
1020 });
1021 auto hv = reduce_data.value(reduce_op);
1022 return std::make_pair(amrex::get<0>(hv), amrex::get<1>(hv));
1023}
1024
1025template <typename T, std::integral N, typename F>
1026requires (!std::same_as<T*,std::decay_t<F>>)
1027std::pair<T,T> MinMax (N n, F const& f)
1028{
1030 ReduceData<T,T> reduce_data(reduce_op);
1031 using ReduceTuple = typename decltype(reduce_data)::Type;
1032 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple {
1033 T tmp = f(i);
1034 return {tmp,tmp};
1035 });
1036 auto hv = reduce_data.value(reduce_op);
1037 return std::make_pair(amrex::get<0>(hv), amrex::get<1>(hv));
1038}
1039
1040template <typename T, std::integral N, typename P>
1041bool AnyOf (N n, T const* v, P const& pred)
1042{
1043 Gpu::LaunchSafeGuard lsg(true);
1045 int* dp = ds.dataPtr();
1046 auto ec = Gpu::ExecutionConfig(n);
1047 ec.numBlocks.x = std::min(ec.numBlocks.x, Gpu::Device::maxBlocksPerLaunch());
1048
1049#ifdef AMREX_USE_SYCL
1050 const int num_ints = std::max(Gpu::Device::warp_size, int(ec.numThreads.x)/Gpu::Device::warp_size) + 1;
1051 const std::size_t shared_mem_bytes = num_ints*sizeof(int);
1052 amrex::launch<AMREX_GPU_MAX_THREADS>(ec.numBlocks.x, shared_mem_bytes, Gpu::gpuStream(),
1053 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept {
1054 int* has_any = &(static_cast<int*>(gh.sharedMemory())[num_ints-1]);
1055 if (gh.threadIdx() == 0) { *has_any = *dp; }
1056 gh.sharedBarrier();
1057
1058 if (!(*has_any))
1059 {
1060 int r = false;
1061 for (N i = AMREX_GPU_MAX_THREADS*gh.blockIdx()+gh.threadIdx(), stride = AMREX_GPU_MAX_THREADS*gh.gridDim();
1062 i < n && !r; i += stride)
1063 {
1064 r = pred(v[i]) ? 1 : 0;
1065 }
1066
1067 r = Gpu::blockReduce<Gpu::Device::warp_size>
1068 (r, Gpu::warpReduce<Gpu::Device::warp_size,int,amrex::Plus<int> >(), 0, gh);
1069 if (gh.threadIdx() == 0 && r) { *dp = 1; }
1070 }
1071 });
1072#else
1073 amrex::launch<AMREX_GPU_MAX_THREADS>(ec.numBlocks.x, 0, Gpu::gpuStream(),
1074 [=] AMREX_GPU_DEVICE () noexcept {
1075 __shared__ int has_any;
1076 if (threadIdx.x == 0) { has_any = *dp; }
1077 __syncthreads();
1078
1079 if (!has_any)
1080 {
1081 int r = false;
1082 for (N i = AMREX_GPU_MAX_THREADS*blockIdx.x+threadIdx.x, stride = AMREX_GPU_MAX_THREADS*gridDim.x;
1083 i < n && !r; i += stride)
1084 {
1085 r = pred(v[i]) ? 1 : 0;
1086 }
1087 r = Gpu::blockReduce<Gpu::Device::warp_size>
1088 (r, Gpu::warpReduce<Gpu::Device::warp_size,int,amrex::Plus<int> >(), 0);
1089 if (threadIdx.x == 0 && r) *dp = 1;
1090 }
1091 });
1092#endif
1093 return ds.dataValue();
1094}
1095
1096template <typename P, int dim>
1097bool AnyOf (BoxND<dim> const& box, P const& pred)
1098{
1099 Gpu::LaunchSafeGuard lsg(true);
1101 int* dp = ds.dataPtr();
1102 const BoxIndexerND<dim> indexer(box);
1103 auto ec = Gpu::ExecutionConfig(box.numPts());
1104 ec.numBlocks.x = std::min(ec.numBlocks.x, Gpu::Device::maxBlocksPerLaunch());
1105
1106#ifdef AMREX_USE_SYCL
1107 const int num_ints = std::max(Gpu::Device::warp_size, int(ec.numThreads.x)/Gpu::Device::warp_size) + 1;
1108 const std::size_t shared_mem_bytes = num_ints*sizeof(int);
1109 amrex::launch<AMREX_GPU_MAX_THREADS>(ec.numBlocks.x, shared_mem_bytes, Gpu::gpuStream(),
1110 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept {
1111 int* has_any = &(static_cast<int*>(gh.sharedMemory())[num_ints-1]);
1112 if (gh.threadIdx() == 0) { *has_any = *dp; }
1113 gh.sharedBarrier();
1114
1115 if (!(*has_any))
1116 {
1117 int r = false;
1118 for (std::uint64_t icell = std::uint64_t(AMREX_GPU_MAX_THREADS)*gh.blockIdx()+gh.threadIdx(),
1119 stride = std::uint64_t(AMREX_GPU_MAX_THREADS)*gh.gridDim();
1120 icell < indexer.numPts() && !r;
1121 icell += stride)
1122 {
1123 auto iv = indexer.intVect(icell);
1124 r = amrex::detail::call_f_intvect(pred, iv) ? 1 : 0;
1125 }
1126 r = Gpu::blockReduce<Gpu::Device::warp_size>
1127 (r, Gpu::warpReduce<Gpu::Device::warp_size,int,amrex::Plus<int> >(), 0, gh);
1128 if (gh.threadIdx() == 0 && r) { *dp = 1; }
1129 }
1130 });
1131#else
1132 AMREX_LAUNCH_KERNEL(AMREX_GPU_MAX_THREADS, ec.numBlocks, ec.numThreads, 0,
1133 Gpu::gpuStream(),
1134 [=] AMREX_GPU_DEVICE () noexcept {
1135 __shared__ int has_any;
1136 if (threadIdx.x == 0) { has_any = *dp; }
1137 __syncthreads();
1138
1139 if (!has_any)
1140 {
1141 int r = false;
1142 for (std::uint64_t icell = std::uint64_t(AMREX_GPU_MAX_THREADS)*blockIdx.x+threadIdx.x,
1143 stride = std::uint64_t(AMREX_GPU_MAX_THREADS)*gridDim.x;
1144 icell < indexer.numPts() && !r;
1145 icell += stride)
1146 {
1147 auto iv = indexer.intVect(icell);
1148 r = amrex::detail::call_f_intvect(pred, iv) ? 1 : 0;
1149 }
1150 r = Gpu::blockReduce<Gpu::Device::warp_size>
1151 (r, Gpu::warpReduce<Gpu::Device::warp_size,int,amrex::Plus<int> >(), 0);
1152 if (threadIdx.x == 0 && r) *dp = 1;
1153 }
1154 });
1155#endif
1156 return ds.dataValue();
1157}
1158
1159}
1160
1161#else
1162
1163template <typename... Ts>
1164class ReduceData
1165{
1166public:
1167 using Type = GpuTuple<Ts...>;
1168
1169 template <typename... Ps>
1170 explicit ReduceData (ReduceOps<Ps...>& reduce_op)
1171 : m_tuple(OpenMP::in_parallel() ? 1 : OpenMP::get_max_threads()),
1172 m_fn_value([&reduce_op,this] () -> Type { return this->value(reduce_op); })
1173 {
1174 reduce_op.resetResultReadiness();
1175 for (auto& t : m_tuple) {
1176 Reduce::detail::for_each_init<0, Type, Ps...>(t);
1177 }
1178 }
1179
1180 ~ReduceData () = default;
1181 ReduceData (ReduceData<Ts...> const&) = delete;
1182 ReduceData (ReduceData<Ts...> &&) = delete;
1183 void operator= (ReduceData<Ts...> const&) = delete;
1184 void operator= (ReduceData<Ts...> &&) = delete;
1185
1186 Type value () { return m_fn_value(); }
1187
1188 template <typename... Ps>
1189 Type value (ReduceOps<Ps...>& reduce_op)
1190 {
1191 return reduce_op.value(*this);
1192 }
1193
1194 Vector<Type>& reference () { return m_tuple; }
1195
1196 Type& reference (int tid)
1197 {
1198 if (m_tuple.size() == 1) {
1199 // No OpenMP or already inside OpenMP parallel when reduce_data is constructed
1200 return m_tuple[0];
1201 } else {
1202 return m_tuple[tid];
1203 }
1204 }
1205
1206private:
1207 Vector<Type> m_tuple;
1208 std::function<Type()> m_fn_value;
1209};
1210
1211namespace Reduce::detail {
1212
1213 // call_f_intvect
1214
1215 template <typename F, int dim>
1217 auto call_f_intvect (F const& f, IntVectND<dim> iv) noexcept ->
1218 decltype(amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv))
1219 {
1220 return amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv);
1221 }
1222
1223 // call_f_intvect_n
1224
1225 template <typename F, typename T, int dim>
1227 auto call_f_intvect_n (F const& f, IntVectND<dim> iv, T n) noexcept ->
1228 decltype(amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv, n))
1229 {
1230 return amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv, n);
1231 }
1232}
1233
1234template <typename... Ps>
1235class ReduceOps
1236{
1237private:
1238
1239 // call_f_box
1240
1241 template <typename D, typename F, int dim>
1242 requires (std::same_as<std::decay_t<decltype(
1243 Reduce::detail::call_f_intvect(std::declval<F const&>(), IntVectND<dim>()))>,
1244 typename D::Type>)
1246 static void call_f_box (BoxND<dim> const& box, typename D::Type & r, F const& f) noexcept
1247 {
1248 using ReduceTuple = typename D::Type;
1249 For(box,
1250 [&] (IntVectND<dim> iv) {
1251 auto pr = Reduce::detail::call_f_intvect(f, iv);
1252 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, pr);
1253 });
1254 }
1255
1256 template <typename D, typename F, int dim>
1257 requires (std::same_as<std::decay_t<decltype(
1258 std::declval<F const&>()(std::declval<BoxND<dim> const&>()))>,
1259 typename D::Type>)
1261 static void call_f_box (BoxND<dim> const& box, typename D::Type & r, F const& f) noexcept
1262 {
1263 using ReduceTuple = typename D::Type;
1264 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, f(box));
1265 }
1266
1267public:
1268
1269 template <FabArrayType MF, typename D, typename F>
1270 requires (IsCallable<F, int, int, int, int>::value)
1271 void eval (MF const& mf, IntVect const& nghost, D & reduce_data, F const& f)
1272 {
1273 using ReduceTuple = typename D::Type;
1274#ifdef AMREX_USE_OMP
1275#pragma omp parallel
1276#endif
1277 {
1278 ReduceTuple rr;
1279 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rr);
1280 for (MFIter mfi(mf,true); mfi.isValid(); ++mfi) {
1281 Box const& b = mfi.growntilebox(nghost);
1282 const int li = mfi.LocalIndex();
1283 const auto lo = amrex::lbound(b);
1284 const auto hi = amrex::ubound(b);
1285 for (int k = lo.z; k <= hi.z; ++k) {
1286 for (int j = lo.y; j <= hi.y; ++j) {
1287 for (int i = lo.x; i <= hi.x; ++i) {
1288 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rr, f(li,i,j,k));
1289 }}}
1290 }
1291 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(
1292 reduce_data.reference(OpenMP::get_thread_num()), rr);
1293 }
1294 }
1295
1296 template <FabArrayType MF, typename D, typename F>
1297 requires (IsCallable<F, int, int, int, int, int>::value)
1298 void eval (MF const& mf, IntVect const& nghost, int ncomp, D & reduce_data, F const& f)
1299 {
1300 using ReduceTuple = typename D::Type;
1301#ifdef AMREX_USE_OMP
1302#pragma omp parallel
1303#endif
1304 {
1305 ReduceTuple rr;
1306 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rr);
1307 for (MFIter mfi(mf,true); mfi.isValid(); ++mfi) {
1308 Box const& b = mfi.growntilebox(nghost);
1309 const int li = mfi.LocalIndex();
1310 const auto lo = amrex::lbound(b);
1311 const auto hi = amrex::ubound(b);
1312 for (int n = 0; n < ncomp; ++n) {
1313 for (int k = lo.z; k <= hi.z; ++k) {
1314 for (int j = lo.y; j <= hi.y; ++j) {
1315 for (int i = lo.x; i <= hi.x; ++i) {
1316 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rr, f(li,i,j,k,n));
1317 }}}}
1318 }
1319 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(
1320 reduce_data.reference(OpenMP::get_thread_num()), rr);
1321 }
1322 }
1323
1324 template <typename D, typename F, int dim>
1325 void eval (BoxND<dim> const& box, D & reduce_data, F&& f)
1326 {
1327 using ReduceTuple = typename D::Type;
1328 ReduceTuple rr;
1329 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rr);
1330 call_f_box<D>(box, rr, std::forward<F>(f));
1331 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(
1332 reduce_data.reference(OpenMP::get_thread_num()), rr);
1333 }
1334
1335 template <std::integral N, typename D, typename F, int dim>
1336 void eval (BoxND<dim> const& box, N ncomp, D & reduce_data, F const& f)
1337 {
1338 using ReduceTuple = typename D::Type;
1339 ReduceTuple rr;
1340 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rr);
1341 For(box, ncomp,
1342 [&] (IntVectND<dim> iv, int n) {
1343 auto pr = Reduce::detail::call_f_intvect_n(f, iv, n);
1344 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rr, pr);
1345 });
1346 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(
1347 reduce_data.reference(OpenMP::get_thread_num()), rr);
1348 }
1349
1350 template <std::integral N, typename D, typename F>
1351 void eval (N n, D & reduce_data, F const& f)
1352 {
1353 using ReduceTuple = typename D::Type;
1354 ReduceTuple rr;
1355 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rr);
1356 for (N i = 0; i < n; ++i) {
1357 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rr, f(i));
1358 }
1359 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(
1360 reduce_data.reference(OpenMP::get_thread_num()), rr);
1361 }
1362
1363 template <typename D>
1364 typename D::Type value (D & reduce_data)
1365 {
1366 auto& rrv = reduce_data.reference();
1367 if (! m_result_is_ready) {
1368 using ReduceTuple = typename D::Type;
1369 if (rrv.size() > 1) {
1370 for (int i = 1, N = rrv.size(); i < N; ++i) {
1371 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rrv[0], rrv[i]);
1372 }
1373 }
1374 m_result_is_ready = true;
1375 }
1376 return rrv[0];
1377 }
1378
1379private:
1380 template <typename... T> friend class ReduceData;
1381 bool m_result_is_ready = false;
1382 void resetResultReadiness () { m_result_is_ready = false; }
1383};
1384
1385namespace Reduce {
1386
1387template <typename T, std::integral N, typename F>
1388requires (!std::same_as<T*,std::decay_t<F>>)
1389T Sum (N n, F const& f, T init_val)
1390{
1391 T r = init_val;
1392#ifdef AMREX_USE_OMP
1393#pragma omp parallel for reduction(+:r)
1394#endif
1395 for (N i = 0; i < n; ++i) {
1396 r += f(i);
1397 }
1398 return r;
1399}
1400
1401template <typename T, std::integral N>
1402T Sum (N n, T const* v, T init_val)
1403{
1404 return Sum(n, [=] (N i) -> T { return v[i]; }, init_val);
1405}
1406
1407template <typename T, std::integral N, typename F>
1408requires (!std::same_as<T*,std::decay_t<F>>)
1409T Min (N n, F const& f, T init_val)
1410{
1411 T r = init_val;
1412#ifdef AMREX_USE_OMP
1413#pragma omp parallel for reduction(min:r)
1414#endif
1415 for (N i = 0; i < n; ++i) {
1416 r = std::min(r,f(i));
1417 }
1418 return r;
1419}
1420
1421template <typename T, std::integral N>
1422T Min (N n, T const* v, T init_val)
1423{
1424 return Reduce::Min(n, [=] (N i) -> T { return v[i]; }, init_val);
1425}
1426
1427template <typename T, std::integral N, typename F>
1428requires (!std::same_as<T*,std::decay_t<F>>)
1429T Max (N n, F const& f, T init_val)
1430{
1431 T r = init_val;
1432#ifdef AMREX_USE_OMP
1433#pragma omp parallel for reduction(max:r)
1434#endif
1435 for (N i = 0; i < n; ++i) {
1436 r = std::max(r,f(i));
1437 }
1438 return r;
1439}
1440
1441template <typename T, std::integral N>
1442T Max (N n, T const* v, T init_val)
1443{
1444 return Reduce::Max(n, [=] (N i) -> T { return v[i]; }, init_val);
1445}
1446
1447template <typename T, std::integral N, typename F>
1448requires (!std::same_as<T*,std::decay_t<F>>)
1449std::pair<T,T> MinMax (N n, F const& f)
1450{
1451 T r_min = std::numeric_limits<T>::max();
1452 T r_max = std::numeric_limits<T>::lowest();
1453#ifdef AMREX_USE_OMP
1454#pragma omp parallel for reduction(min:r_min) reduction(max:r_max)
1455#endif
1456 for (N i = 0; i < n; ++i) {
1457 T tmp = f(i);
1458 r_min = std::min(r_min,tmp);
1459 r_max = std::max(r_max,tmp);
1460 }
1461 return std::make_pair(r_min,r_max);
1462}
1463
1464template <typename T, std::integral N>
1465std::pair<T,T> MinMax (N n, T const* v)
1466{
1467 return Reduce::MinMax<T>(n, [=] (N i) -> T { return v[i]; });
1468}
1469
1470template <typename T, std::integral N, typename P>
1471bool AnyOf (N n, T const* v, P const& pred)
1472{
1473 return std::any_of(v, v+n, pred);
1474}
1475
1476template <typename P, int dim>
1477bool AnyOf (BoxND<dim> const& box, P const& pred)
1478{
1479 for (auto iv : box.iterator()) { // NOLINT(readability-use-anyofallof)
1480 if (Reduce::detail::call_f_intvect(pred, iv)) { return true; }
1481 }
1482 return false;
1483}
1484
1485}
1486
1487#endif
1488
1493template <typename... Ts, typename... Ps>
1495constexpr GpuTuple<Ts...>
1497{
1498 GpuTuple<Ts...> r{};
1499 Reduce::detail::for_each_init<0, decltype(r), Ps...>(r);
1500 return r;
1501}
1502
1507template <typename... Ts, typename... Ps>
1509constexpr GpuTuple<Ts...>
1511{
1512 GpuTuple<Ts...> r{};
1513 Reduce::detail::for_each_init<0, decltype(r), Ps...>(r);
1514 return r;
1515}
1516
1518template <typename Ops, typename Ts>
1519class ReducerImpl;
1520
1521template <typename... Ops, typename... Ts>
1522class ReducerImpl<TypeList<Ops...>, TypeList<Ts...>>
1523{
1524public:
1525 static_assert(sizeof...(Ops) > 0);
1526 static_assert(sizeof...(Ts) > 0);
1527 static_assert(sizeof...(Ops) == sizeof...(Ts));
1528
1529 ReducerImpl ()
1530 : m_reduce_data(m_reduce_op)
1531 {}
1532
1533protected:
1534 using Result_t = GpuTuple<Ts...>;
1535 ReduceOps<Ops...> m_reduce_op;
1536 ReduceData<Ts...> m_reduce_data;
1537};
1539
1604template <typename Ops, typename Ts>
1606 : public ReducerImpl<ToTypeList_t<Ops>, ToTypeList_t<Ts>>
1607{
1608 using Base = ReducerImpl<ToTypeList_t<Ops>, ToTypeList_t<Ts>>;
1609public:
1610
1611 using Result_t = typename Base::Result_t;
1612 static constexpr int size = GpuTupleSize<Result_t>::value;
1613
1614 Reducer () = default;
1615 ~Reducer () = default;
1616
1618 Reducer (Reducer const&) = delete;
1619 Reducer (Reducer &&) = delete;
1620 void operator= (Reducer const&) = delete;
1621 void operator= (Reducer &&) = delete;
1623
1637 template <typename F, int dim>
1640 void eval (BoxND<dim> const& box, F&& f)
1641 {
1642 this->m_reduce_op.eval(box, this->m_reduce_data, std::forward<F>(f));
1643 }
1644
1660 template <typename F, int dim>
1662 IsCallable<F, IntVectND<dim>, int>::value)
1663 void eval (BoxND<dim> const& box, int ncomp, F&& f)
1664 {
1665 this->m_reduce_op.eval(box, ncomp, this->m_reduce_data, std::forward<F>(f));
1666 }
1667
1687 template <FabArrayType MF, typename F>
1689 void eval (MF const& mf, IntVect const& nghost, F && f)
1690 {
1691 this->m_reduce_op.eval(mf, nghost, this->m_reduce_data, std::forward<F>(f));
1692 }
1693
1716 template <FabArrayType MF, typename F>
1718 void eval (MF const& mf, IntVect const& nghost, int ncomp, F && f)
1719 {
1720 this->m_reduce_op.eval(mf, nghost, ncomp, this->m_reduce_data, std::forward<F>(f));
1721 }
1722
1735 template <typename N, typename F>
1736 requires (IsCallable<F, N>::value)
1737 void eval (N n, F && f)
1738 {
1739 this->m_reduce_op.eval(n, this->m_reduce_data, std::forward<F>(f));
1740 }
1741
1752 [[nodiscard]] Result_t getResult ()
1753 {
1754 return this->m_reduce_data.value(this->m_reduce_op);
1755 }
1756};
1757
1758}
1759
1760#endif
Memory arena base class and global arena accessors.
#define AMREX_ALWAYS_ASSERT_WITH_MESSAGE(EX, MSG)
Definition AMReX_BLassert.H:49
#define AMREX_ASSERT(EX)
Definition AMReX_BLassert.H:38
#define AMREX_FORCE_INLINE
Definition AMReX_Extension.H:124
#define AMREX_GPU_MAX_STREAMS
Definition AMReX_GpuDevice.H:21
#define AMREX_LAUNCH_KERNEL(MT, blocks, threads, sharedMem, stream,...)
Definition AMReX_GpuLaunch.H:37
#define AMREX_GPU_DEVICE
Definition AMReX_GpuQualifiers.H:18
#define AMREX_GPU_HOST_DEVICE
Definition AMReX_GpuQualifiers.H:20
Convenience header for the core AMReX GPU facilities.
Real * pdst
Definition AMReX_HypreMLABecLap.cpp:1132
virtual void free(void *pt)=0
Free a previously allocated block pointed to by pt.
A Rectangular Domain on an Integer Lattice.
Definition AMReX_Box.H:54
__host__ __device__ bool isEmpty() const noexcept
Checks if it is an empty BoxND.
Definition AMReX_Box.H:223
__host__ __device__ Long numPts() const noexcept
Return the number of points contained in the BoxND.
Definition AMReX_Box.H:385
__host__ __device__ IndexTypeND< dim > ixType() const noexcept
Return the indexing type.
Definition AMReX_Box.H:148
GPU-compatible tuple.
Definition AMReX_Tuple.H:104
static int streamIndex(gpuStream_t s=gpuStream()) noexcept
Definition AMReX_GpuDevice.cpp:715
static bool usingExternalStream() noexcept
Definition AMReX_GpuDevice.cpp:842
Cell-Based or Node-Based Indices.
Definition AMReX_IndexType.H:36
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:176
Dynamically allocated vector for trivially copyable data.
Definition AMReX_PODVector.H:308
T * data() noexcept
Definition AMReX_PODVector.H:672
Definition AMReX_Reduce.H:438
~ReduceData()
Definition AMReX_Reduce.H:460
int maxStreamIndex() const
Definition AMReX_Reduce.H:500
Type value()
Definition AMReX_Reduce.H:473
void updateMaxStreamIndex(gpuStream_t const &s)
Definition AMReX_Reduce.H:501
int & nBlocks(gpuStream_t const &s)
Definition AMReX_Reduce.H:496
ReduceData(ReduceOps< Ps... > &reduce_op)
Definition AMReX_Reduce.H:443
void markValueCalled() noexcept
Definition AMReX_Reduce.H:505
Type * devicePtr(gpuStream_t const &s)
Definition AMReX_Reduce.H:489
Type value(ReduceOps< Ps... > &reduce_op)
Definition AMReX_Reduce.H:481
Type * devicePtr()
Definition AMReX_Reduce.H:488
GpuArray< int, 8 > & nBlocks()
Definition AMReX_Reduce.H:495
ReduceData(ReduceData< Ts... > const &)=delete
Type * hostPtr()
Definition AMReX_Reduce.H:493
int maxBlocks() const
Definition AMReX_Reduce.H:498
ReduceData(ReduceData< Ts... > &&)=delete
Definition AMReX_Reduce.H:597
void eval(BoxND< dim > const &box, N ncomp, D &reduce_data, F const &f)
Definition AMReX_Reduce.H:791
D::Type value(D &reduce_data)
Definition AMReX_Reduce.H:848
void eval(BoxND< dim > const &box, D &reduce_data, F const &f)
Definition AMReX_Reduce.H:785
void eval(MF const &mf, IntVect const &nghost, D &reduce_data, F &&f)
Definition AMReX_Reduce.H:734
void eval(N n, D &reduce_data, F const &f)
Definition AMReX_Reduce.H:797
Class for local reductions (e.g., sum, min and max).
Definition AMReX_Reduce.H:1607
Reducer()=default
Result_t getResult()
Get the final reduction result.
Definition AMReX_Reduce.H:1752
typename Base::Result_t Result_t
Reduction result type, GpuTuple<U...>, where U... are the types in Ts.
Definition AMReX_Reduce.H:1611
~Reducer()=default
void eval(BoxND< dim > const &box, F &&f)
Reduction over a Box.
Definition AMReX_Reduce.H:1640
amrex_long Long
Definition AMReX_INT.H:30
T Min(N n, T const *v, T init_val=std::numeric_limits< T >::max())
Compute the minimum of an array of values.
Definition AMReX_Reduce.H:967
bool AnyOf(N n, T const *v, P const &pred)
Test whether any element in an array satisfies a unary predicate.
Definition AMReX_Reduce.H:1041
std::pair< T, T > MinMax(N n, T const *v)
Compute the minimum and maximum of an array of values.
Definition AMReX_Reduce.H:1013
T Max(N n, T const *v, T init_val=std::numeric_limits< T >::lowest())
Compute the maximum of an array of values.
Definition AMReX_Reduce.H:990
T Sum(N n, T const *v, T init_val=0)
Compute the sum of an array of values.
Definition AMReX_Reduce.H:944
__host__ __device__ Dim3 ubound(Array4< T > const &a) noexcept
Return the inclusive upper bounds of an Array4 in Dim3 form.
Definition AMReX_Array4.H:1365
__host__ __device__ Dim3 lbound(Array4< T > const &a) noexcept
Return the inclusive lower bounds of an Array4 in Dim3 form.
Definition AMReX_Array4.H:1351
__host__ __device__ BoxND< dim > grow(const BoxND< dim > &b, int i) noexcept
Return a copy of b grown uniformly by i cells in every direction.
Definition AMReX_Box.H:1326
Arena * The_Pinned_Arena()
Definition AMReX_Arena.cpp:855
Arena * The_Arena()
Definition AMReX_Arena.cpp:815
void Sum(Gpu::DeviceVector< T > &v, MPI_Comm comm)
Definition AMReX_GpuParallelReduce.H:37
__host__ __device__ constexpr const T & min(const T &a, const T &b) noexcept
Definition AMReX_Algorithm.H:31
__host__ __device__ constexpr const T & max(const T &a, const T &b) noexcept
Definition AMReX_Algorithm.H:53
__host__ __device__ AMREX_FORCE_INLINE T Max(T *const m, T const value) noexcept
Definition AMReX_GpuAtomic.H:419
__host__ __device__ AMREX_FORCE_INLINE T Min(T *const m, T const value) noexcept
Definition AMReX_GpuAtomic.H:356
__device__ int blockReduceLogicalOr(int source) noexcept
Definition AMReX_GpuReduce.H:559
__device__ T blockReduceMax(T source) noexcept
Definition AMReX_GpuReduce.H:458
__device__ T blockReduceMin(T source) noexcept
Definition AMReX_GpuReduce.H:403
__device__ int blockReduceLogicalAnd(int source) noexcept
Definition AMReX_GpuReduce.H:511
__device__ T blockReduceSum(T source) noexcept
Definition AMReX_GpuReduce.H:353
Definition AMReX_Amr.cpp:50
__host__ __device__ void ignore_unused(const Ts &...)
No-op helper that marks variables as intentionally unused.
Definition AMReX.H:259
void For(TypeList< CTOs... > ctos, std::array< int, sizeof...(CTOs)> const &runtime_options, T N, F &&f)
Definition AMReX_CTOParallelForImpl.H:400
cudaStream_t gpuStream_t
Definition AMReX_GpuControl.H:79
__host__ __device__ constexpr GpuTuple< Ts... > IdentityTuple(GpuTuple< Ts... >, ReduceOps< Ps... >) noexcept
Return a GpuTuple containing the identity element for each operation in ReduceOps....
Definition AMReX_Reduce.H:1496
BoxND< 3 > Box
Box is an alias for amrex::BoxND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:35
typename ToTypeList< T >::type ToTypeList_t
Definition AMReX_TypeList.H:233
const int[]
Definition AMReX_BLProfiler.cpp:1665
Utility that maps flattened point indices back to IntVectND coordinates.
Definition AMReX_Box.H:2494
__host__ __device__ IntVectND< dim > intVect(std::uint64_t icell) const
Convert flattened point index icell to its IntVectND coordinate.
Definition AMReX_Box.H:2517
__host__ __device__ std::uint64_t numPts() const
Return the number of points covered by the indexed box.
Definition AMReX_Box.H:2552
Fixed-size array that can be used on GPU.
Definition AMReX_Array.H:52
Definition AMReX_Tuple.H:133
Definition AMReX_GpuMemory.H:57
T * dataPtr()
Definition AMReX_GpuMemory.H:91
T dataValue() const
Definition AMReX_GpuMemory.H:93
Definition AMReX_GpuLaunch.H:121
Definition AMReX_GpuTypes.H:88
Definition AMReX_GpuControl.H:127
Definition AMReX_GpuReduce.H:290
Test if a given type T is callable with arguments of type Args...
Definition AMReX_TypeTraits.H:208
Function object that returns the sum of two values.
Definition AMReX_Functional.H:23
Definition AMReX_Reduce.H:375
__host__ __device__ void local_update(T &d, T s) const noexcept
Definition AMReX_Reduce.H:396
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:399
__device__ void parallel_update(T &d, T s) const noexcept
Definition AMReX_Reduce.H:387
Definition AMReX_Reduce.H:404
__device__ void parallel_update(T &d, T s) const noexcept
Definition AMReX_Reduce.H:416
__host__ __device__ void local_update(T &d, T s) const noexcept
Definition AMReX_Reduce.H:425
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:428
Definition AMReX_Reduce.H:341
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:366
__host__ __device__ void local_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:362
__device__ void parallel_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:353
Definition AMReX_Reduce.H:307
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:332
__device__ void parallel_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:319
__host__ __device__ void local_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:328
Definition AMReX_Reduce.H:277
__device__ void parallel_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:290
__host__ __device__ void local_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:299
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:302
Struct for holding types.
Definition AMReX_TypeList.H:13