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#include <AMReX.H>
5
6#include <AMReX_Concepts.H>
7#include <AMReX_Gpu.H>
8#include <AMReX_Arena.H>
9#include <AMReX_OpenMP.H>
10#include <AMReX_MFIter.H>
11#include <AMReX_TypeList.H>
12#include <AMReX_ValLocPair.H>
13
14#include <algorithm>
15#include <concepts>
16#include <functional>
17#include <limits>
18#include <utility>
19
20namespace amrex {
21
22namespace Reduce {
23
24// The declaration of these functions are here to work around doxygen issues.
25
40template <typename T, std::integral N>
41T Sum (N n, T const* v, T init_val = 0);
42
59template <typename T, std::integral N, typename F>
60requires (!std::same_as<T*,std::decay_t<F>>)
61T Sum (N n, F const& f, T init_val = 0);
62
77template <typename T, std::integral N>
78T Min (N n, T const* v, T init_val = std::numeric_limits<T>::max());
79
96template <typename T, std::integral N, typename F>
97requires (!std::same_as<T*,std::decay_t<F>>)
98T Min (N n, F const& f, T init_val = std::numeric_limits<T>::max());
99
114template <typename T, std::integral N>
115T Max (N n, T const* v, T init_val = std::numeric_limits<T>::lowest());
116
133template <typename T, std::integral N, typename F>
134requires (!std::same_as<T*,std::decay_t<F>>)
135T Max (N n, F const& f, T init_val = std::numeric_limits<T>::lowest());
136
150template <typename T, std::integral N>
151std::pair<T,T> MinMax (N n, T const* v);
152
168template <typename T, std::integral N, typename F>
169requires (!std::same_as<T*,std::decay_t<F>>)
170std::pair<T,T> MinMax (N n, F const& f);
171
187template <typename T, std::integral N, typename P>
188bool AnyOf (N n, T const* v, P const& pred);
189
203template <typename P, int dim>
204bool AnyOf (BoxND<dim> const& box, P const& pred);
205
206}
207
209namespace Reduce::detail {
210
211// CPU reductions keep nacc partial accumulators so that the inner loop
212// vectorizes.
213inline constexpr int nacc = 8;
214
215// Pragmas for the loop over the nacc accumulators. With GCC, `unroll 4`
216// keeps it vectorizable and novector plus a full unroll keeps it scalar on
217// purpose. Clang needs neither, and nvcc does not know them.
218#if defined(AMREX_CXX_GCC) && !defined(__CUDACC__)
219#define AMREX_PRAGMA_NACC_VECTORIZE _Pragma("GCC unroll 4")
220#if __GNUC__ >= 14
221#define AMREX_PRAGMA_NACC_NOVECTOR _Pragma("GCC novector")
222#else
223#define AMREX_PRAGMA_NACC_NOVECTOR
224#endif
225#define AMREX_PRAGMA_NACC_SCALAR AMREX_PRAGMA_NACC_NOVECTOR _Pragma("GCC unroll 8")
226#else
227#define AMREX_PRAGMA_NACC_VECTORIZE
228#define AMREX_PRAGMA_NACC_NOVECTOR
229#define AMREX_PRAGMA_NACC_SCALAR
230#endif
231static_assert(nacc == 8, "The AMREX_PRAGMA_NACC unroll factors assume nacc == 8");
232
233// Split a 1D range into chunks of a multiple of nacc, enough for all threads.
234template <std::integral N>
235std::pair<N,N> chunks_1d (N n)
236{
237 auto const nthreads = OpenMP::get_max_threads();
238 N const chunk = std::max(static_cast<N>(nacc), static_cast<N>((n / (4*nthreads) / nacc) * nacc));
239 N const nchunks = n / chunk + ((n % chunk != 0) ? 1 : 0);
240 return {chunk, nchunks};
241}
242
243#ifdef AMREX_USE_GPU
244#ifdef AMREX_USE_SYCL
245 template <std::size_t Idx, typename T, typename P>
247 void for_each_parallel (T& d, T const& s, Gpu::Handler const& h)
248 {
249 P().parallel_update(amrex::get<Idx>(d), amrex::get<Idx>(s), h);
250 }
251
252 template <std::size_t Idx, typename T, typename P, typename P1, typename... Ps>
254 void for_each_parallel (T& d, T const& s, Gpu::Handler const& h)
255 {
256 P().parallel_update(amrex::get<Idx>(d), amrex::get<Idx>(s), h);
257 for_each_parallel<Idx+1,T,P1,Ps...>(d, s, h);
258 }
259#else
260 template <std::size_t Idx, typename T, typename P>
262 void for_each_parallel (T& d, T const& s)
263 {
264 P().parallel_update(amrex::get<Idx>(d), amrex::get<Idx>(s));
265 }
266
267 template <std::size_t Idx, typename T, typename P, typename P1, typename... Ps>
269 void for_each_parallel (T& d, T const& s)
270 {
271 P().parallel_update(amrex::get<Idx>(d), amrex::get<Idx>(s));
272 for_each_parallel<Idx+1,T,P1,Ps...>(d, s);
273 }
274#endif
275#endif
276
277 template <std::size_t Idx, typename T, typename P>
279 void for_each_local (T& d, T const& s)
280 {
281 P().local_update(amrex::get<Idx>(d), amrex::get<Idx>(s));
282 }
283
284 template <std::size_t Idx, typename T, typename P, typename P1, typename... Ps>
286 void for_each_local (T& d, T const& s)
287 {
288 P().local_update(amrex::get<Idx>(d), amrex::get<Idx>(s));
289 for_each_local<Idx+1,T,P1,Ps...>(d, s);
290 }
291
292 template <std::size_t Idx, typename T, typename P>
294 constexpr void for_each_init (T& t)
295 {
296 P().init(amrex::get<Idx>(t));
297 }
298
299 template <std::size_t Idx, typename T, typename P, typename P1, typename... Ps>
301 constexpr void for_each_init (T& t)
302 {
303 P().init(amrex::get<Idx>(t));
304 for_each_init<Idx+1,T,P1,Ps...>(t);
305 }
306}
308
311{
312
313#ifdef AMREX_USE_GPU
314#ifdef AMREX_USE_SYCL
315 template <typename T>
317 void parallel_update (T& d, T const& s, Gpu::Handler const& h) const noexcept {
318 T r = Gpu::blockReduceSum(s,h);
319 if (h.threadIdx() == 0) { d += r; }
320 }
321#else
322 template <typename T, int MT=AMREX_GPU_MAX_THREADS>
324 void parallel_update (T& d, T const& s) const noexcept {
326 if (threadIdx.x == 0) { d += r; }
327 }
328#endif
329#endif
330
331 template <typename T>
333 void local_update (T& d, T const& s) const noexcept { d += s; }
334
335 template <typename T>
336 constexpr void init (T& t) const noexcept { t = 0; }
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::blockReduceMin(s,h);
348 if (h.threadIdx() == 0) { d = amrex::min(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 {
355 if (threadIdx.x == 0) { d = amrex::min(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::min(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>::max(); }
367
368 template <typename T>
369 requires (!std::numeric_limits<T>::is_specialized)
370 constexpr void init (T& t) const noexcept { t = T::max(); }
371};
372
375{
376#ifdef AMREX_USE_GPU
377#ifdef AMREX_USE_SYCL
378 template <typename T>
380 void parallel_update (T& d, T const& s, Gpu::Handler const& h) const noexcept {
381 T r = Gpu::blockReduceMax(s,h);
382 if (h.threadIdx() == 0) { d = amrex::max(d,r); }
383 }
384#else
385 template <typename T, int MT=AMREX_GPU_MAX_THREADS>
387 void parallel_update (T& d, T const& s) const noexcept {
389 if (threadIdx.x == 0) { d = amrex::max(d,r); }
390 }
391#endif
392#endif
393
394 template <typename T>
396 void local_update (T& d, T const& s) const noexcept { d = amrex::max(d,s); }
397
398 template <typename T>
399 requires (std::numeric_limits<T>::is_specialized)
400 constexpr void init (T& t) const noexcept { t = std::numeric_limits<T>::lowest(); }
401
402 template <typename T>
403 requires (!std::numeric_limits<T>::is_specialized)
404 constexpr void init (T& t) const noexcept { t = T::lowest(); }
405};
406
409{
410#ifdef AMREX_USE_GPU
411#ifdef AMREX_USE_SYCL
412 template <std::integral T>
414 void parallel_update (T& d, T s, Gpu::Handler const& h) const noexcept {
416 if (h.threadIdx() == 0) { d = d && r; }
417 }
418#else
419 template <std::integral T, int MT=AMREX_GPU_MAX_THREADS>
421 void parallel_update (T& d, T s) const noexcept {
423 if (threadIdx.x == 0) { d = d && r; }
424 }
425#endif
426#endif
427
428 template <std::integral T>
430 void local_update (T& d, T s) const noexcept { d = d && s; }
431
432 template <std::integral T>
433 constexpr void init (T& t) const noexcept { t = true; }
434};
435
438{
439#ifdef AMREX_USE_GPU
440#ifdef AMREX_USE_SYCL
441 template <std::integral T>
443 void parallel_update (T& d, T s, Gpu::Handler const& h) const noexcept {
444 T r = Gpu::blockReduceLogicalOr(s,h);
445 if (h.threadIdx() == 0) { d = d || r; }
446 }
447#else
448 template <std::integral T, int MT=AMREX_GPU_MAX_THREADS>
450 void parallel_update (T& d, T s) const noexcept {
452 if (threadIdx.x == 0) { d = d || r; }
453 }
454#endif
455#endif
456
457 template <std::integral T>
459 void local_update (T& d, T s) const noexcept { d = d || s; }
460
461 template <std::integral T>
462 constexpr void init (T& t) const noexcept { t = false; }
463};
464
466namespace Reduce::detail {
467// Whether the accumulator loop of a CPU reduction over Ps is vectorized.
468// A lone min or max stays scalar with GCC 14 or newer on x86 without
469// SSE4.1, where the vector form is slower. Older GCC has no novector.
470template <typename... Ps>
471inline constexpr bool vectorize_nacc_loop =
472#if defined(AMREX_CXX_GCC) && !defined(__CUDACC__) && (__GNUC__ >= 14) && \
473 (defined(__x86_64__) || defined(__i386__)) && !defined(__SSE4_1__)
474 sizeof...(Ps) != 1 ||
475 !((std::is_same_v<Ps,ReduceOpMin> || std::is_same_v<Ps,ReduceOpMax>) && ...);
476#else
477 true;
478#endif
479
480// The nacc partial accumulators of a CPU reduction. GCC keeps them in
481// vector registers only when each tuple member has its own array, clang
482// only when each lane holds a whole tuple.
483template <typename Tup> struct Lanes;
484
485template <typename... Ts>
486struct Lanes<GpuTuple<Ts...>>
487{
488#ifdef AMREX_CXX_GCC
489 GpuTuple<GpuArray<Ts,nacc>...> data;
490 template <int K> auto& get (int lane) noexcept { return amrex::get<K>(data)[lane]; }
491 template <int K> [[nodiscard]] auto const& get (int lane) const noexcept { return amrex::get<K>(data)[lane]; }
492#else
493 GpuTuple<Ts...> data[nacc];
494 template <int K> auto& get (int lane) noexcept { return amrex::get<K>(data[lane]); }
495 template <int K> [[nodiscard]] auto const& get (int lane) const noexcept { return amrex::get<K>(data[lane]); }
496#endif
497};
498
499template <int K, typename Tup>
501void lanes_set (Lanes<Tup>& acc, Tup const& v)
502{
503 for (int lane = 0; lane < nacc; ++lane) { acc.template get<K>(lane) = amrex::get<K>(v); }
504 if constexpr (K+1 < GpuTupleSize<Tup>::value) { lanes_set<K+1>(acc, v); }
505}
506
507template <int K, typename Tup, typename P, typename... Ps>
509void lanes_update (Lanes<Tup>& acc, int lane, Tup const& s)
510{
511 P().local_update(acc.template get<K>(lane), amrex::get<K>(s));
512 if constexpr (sizeof...(Ps) > 0) { lanes_update<K+1,Tup,Ps...>(acc, lane, s); }
513}
514
515template <int K, typename Tup, typename P, typename... Ps>
517void lanes_fold (Lanes<Tup> const& acc, Tup& r)
518{
519 for (int lane = 0; lane < nacc; ++lane) { P().local_update(amrex::get<K>(r), acc.template get<K>(lane)); }
520 if constexpr (sizeof...(Ps) > 0) { lanes_fold<K+1,Tup,Ps...>(acc, r); }
521}
522
523// One row of a CPU reduction over the ops Ps: full blocks of nacc cells go
524// into the lanes, the rest into rem. value(i) is the tuple for cell i.
525template <typename... Ps, typename Tup, std::integral N, typename F>
527void reduce_row (N ilo, N ihi, Lanes<Tup>& acc, Tup& rem, F const& value)
528{
529 N i = ilo;
530 AMREX_PRAGMA_NACC_NOVECTOR
531 for (; i + (nacc-1) <= ihi; i += nacc) {
532 if constexpr (vectorize_nacc_loop<Ps...>) {
533 AMREX_PRAGMA_NACC_VECTORIZE
534 for (int lane = 0; lane < nacc; ++lane) { lanes_update<0,Tup,Ps...>(acc, lane, value(static_cast<N>(i+lane))); }
535 } else {
536 AMREX_PRAGMA_NACC_SCALAR
537 for (int lane = 0; lane < nacc; ++lane) { lanes_update<0,Tup,Ps...>(acc, lane, value(static_cast<N>(i+lane))); }
538 }
539 }
540 AMREX_PRAGMA_NACC_NOVECTOR
541 for (; i <= ihi; ++i) { for_each_local<0,Tup,Ps...>(rem, value(i)); }
542}
543}
544#undef AMREX_PRAGMA_NACC_VECTORIZE
545#undef AMREX_PRAGMA_NACC_NOVECTOR
546#undef AMREX_PRAGMA_NACC_SCALAR
548
549template <typename... Ps> class ReduceOps;
550
551#ifdef AMREX_USE_GPU
552
554template <typename... Ts>
556{
557public:
558 using Type = GpuTuple<Ts...>;
559
560 template <typename... Ps>
561 explicit ReduceData (ReduceOps<Ps...>& reduce_op)
562 : m_max_blocks(Gpu::Device::maxBlocksPerLaunch()),
563 m_host_tuple((Type*)(The_Pinned_Arena()->alloc(sizeof(Type)))),
564 m_device_tuple((Type*)(The_Arena()->alloc((AMREX_GPU_MAX_STREAMS)
565 * m_max_blocks * sizeof(Type)))),
566 m_fn_value([&reduce_op,this] () -> Type { return this->value(reduce_op); })
567 {
568 reduce_op.resetResultReadiness();
569 static_assert(std::is_trivially_copyable<Type>(),
570 "ReduceData::Type must be trivially copyable");
571 static_assert(std::is_trivially_destructible<Type>(),
572 "ReduceData::Type must be trivially destructible");
573
574 new (m_host_tuple) Type();
575 m_nblocks.fill(0);
576 }
577
580 !m_used_external_stream || m_value_called,
581 "ReduceData used on an external GPU stream must call value() before destruction.");
582 The_Pinned_Arena()->free(m_host_tuple);
583 The_Arena()->free(m_device_tuple);
584 }
585
586 ReduceData (ReduceData<Ts...> const&) = delete;
588 void operator= (ReduceData<Ts...> const&) = delete;
589 void operator= (ReduceData<Ts...> &&) = delete;
590
592 {
593 Type r = m_fn_value();
594 m_value_called = true;
595 return r;
596 }
597
598 template <typename... Ps>
600 {
601 Type r = reduce_op.value(*this);
602 m_value_called = true;
603 return r;
604 }
605
606 Type* devicePtr () { return m_device_tuple; }
608 return m_device_tuple+streamIndexChecked(s)*m_max_blocks;
609 }
610
611 Type* hostPtr () { return m_host_tuple; }
612
614 int& nBlocks (gpuStream_t const& s) { return m_nblocks[streamIndexChecked(s)]; }
615
616 int maxBlocks () const { return m_max_blocks; }
617
618 int maxStreamIndex () const { return m_max_stream_index; }
620 m_max_stream_index = std::max(m_max_stream_index,streamIndexChecked(s));
621 }
622
623 void markValueCalled () noexcept { m_value_called = true; }
624
625private:
626 int streamIndexChecked (gpuStream_t const& s)
627 {
628 int const idx = Gpu::Device::streamIndex(s);
629 m_used_external_stream = m_used_external_stream || Gpu::Device::usingExternalStream();
630 if (idx == 0) {
631 if (m_stream_index_zero_set) {
632 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_stream_index_zero == s,
633 "ReduceData cannot be reused across different external GPU streams "
634 "or between an external GPU stream and AMReX stream 0.");
635 } else {
636 m_stream_index_zero = s;
637 m_stream_index_zero_set = true;
638 }
639 }
640 return idx;
641 }
642
643 int m_max_blocks;
644 int m_max_stream_index = 0;
645 Type* m_host_tuple = nullptr;
646 Type* m_device_tuple = nullptr;
648 gpuStream_t m_stream_index_zero{};
649 bool m_stream_index_zero_set = false;
650 bool m_used_external_stream = false;
651 bool m_value_called = false;
652 std::function<Type()> m_fn_value;
653};
654
656namespace Reduce::detail {
657
658 // call_f_intvect_box
659
660 template <typename F, int dim>
662 auto call_f_intvect_box (F const& f, IntVectND<dim> iv, IndexTypeND<dim>) noexcept ->
663 decltype(amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv))
664 {
665 return amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv);
666 }
667
668 template <typename F, int dim>
670 auto call_f_intvect_box (F const& f, IntVectND<dim> iv, IndexTypeND<dim> t) noexcept ->
671 decltype(f(BoxND<dim>(iv, iv, t)))
672 {
673 return f(BoxND<dim>(iv, iv, t));
674 }
675
676 // call_f_intvect_n
677 template <typename F, typename T, int dim>
679 auto call_f_intvect_n (F const& f, IntVectND<dim> iv, T n) noexcept ->
680 decltype(amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv, n))
681 {
682 return amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv, n);
683 }
684
685 // mf_call_f
686
687 struct iterate_box {};
688 struct iterate_box_comp {};
689
690 template <typename Tag, typename F, typename T, typename... Ps>
691 requires (std::same_as<iterate_box,Tag>)
693 void mf_call_f (F const& f, int ibox, int i, int j, int k, int, T& r) noexcept
694 {
695 auto const& pr = f(ibox,i,j,k);
696 Reduce::detail::for_each_local<0, T, Ps...>(r, pr);
697 }
698
699 template <typename Tag, typename F, typename T, typename... Ps>
700 requires (std::same_as<iterate_box_comp,Tag>)
702 void mf_call_f (F const& f, int ibox, int i, int j, int k, int ncomp, T& r) noexcept
703 {
704 for (int n = 0; n < ncomp; ++n) {
705 auto const& pr = f(ibox,i,j,k,n);
706 Reduce::detail::for_each_local<0, T, Ps...>(r, pr);
707 }
708 }
709}
711
713template <typename... Ps>
715{
716public:
717
719
720 // This is public for CUDA
721 template <typename Tag, typename MF, typename D, typename F>
722 void eval_mf (Tag, MF const& mf, IntVect const& nghost, int ncomp, D& reduce_data, F const& f)
723 {
724 using ReduceTuple = typename D::Type;
725 const int nboxes = mf.local_size();
726 if (nboxes > 0) {
727 auto const& parforinfo = mf.getParForInfo(nghost);
728 auto nblocks_per_box = parforinfo.getNBlocksPerBox(AMREX_GPU_MAX_THREADS);
729 AMREX_ASSERT(Long(nblocks_per_box)*Long(nboxes) < Long(std::numeric_limits<int>::max()));
730 const int nblocks = nblocks_per_box * nboxes;
731 const BoxIndexer* dp_boxes = parforinfo.getBoxes();
732
733 auto const& stream = Gpu::gpuStream();
734 auto pdst = reduce_data.devicePtr(stream);
735 int nblocks_ec = std::min(nblocks, reduce_data.maxBlocks());
736 AMREX_ASSERT(Long(nblocks_ec)*2 <= Long(std::numeric_limits<int>::max()));
737 int& nblocks_ref = reduce_data.nBlocks(stream);
738 auto old_nblocks = static_cast<unsigned int>(nblocks_ref);
739 nblocks_ref = amrex::max(nblocks_ref, nblocks_ec);
740 reduce_data.updateMaxStreamIndex(stream);
741
742#ifdef AMREX_USE_SYCL
743 // device reduce needs local(i.e., shared) memory
744 constexpr std::size_t shared_mem_bytes = sizeof(ReduceTuple)
745 * std::max(Gpu::Device::warp_size, AMREX_GPU_MAX_THREADS/Gpu::Device::warp_size);
746 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, shared_mem_bytes, stream,
747 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
748 {
749 Dim1 blockIdx {gh.blockIdx()};
750 Dim1 threadIdx{gh.threadIdx()};
751#else
753 <<<nblocks_ec, AMREX_GPU_MAX_THREADS, 0, stream>>>
754 ([=] AMREX_GPU_DEVICE () noexcept
755 {
756#endif
757 ReduceTuple r;
758 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
759 ReduceTuple& dst = pdst[blockIdx.x];
760 if (threadIdx.x == 0 && blockIdx.x >= old_nblocks) {
761 dst = r;
762 }
763 for (int iblock = blockIdx.x; iblock < nblocks; iblock += nblocks_ec) {
764 int ibox = iblock / nblocks_per_box;
765 auto icell = std::uint64_t(iblock-ibox*nblocks_per_box)*AMREX_GPU_MAX_THREADS + threadIdx.x;
766
767 BoxIndexer const& indexer = dp_boxes[ibox];
768 if (icell < indexer.numPts()) {
769 auto [i, j, k] = indexer(icell);
770 Reduce::detail::mf_call_f<Tag, F, ReduceTuple, Ps...>
771 (f, ibox, i, j, k, ncomp, r);
772 }
773 }
774#ifdef AMREX_USE_SYCL
775 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r, gh);
776#else
777 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r);
778#endif
779 });
780 }
781 }
782
783 // This is public for CUDA
784 template <typename Tag, int dim, typename D, typename F>
785 void eval_box (Tag, BoxND<dim> const& box, int ncomp, D& reduce_data, F const& f)
786 {
787 if (box.isEmpty()) { return; }
788 using ReduceTuple = typename D::Type;
789 auto const& stream = Gpu::gpuStream();
790 auto dp = reduce_data.devicePtr(stream);
791 int& nblocks = reduce_data.nBlocks(stream);
792 const BoxIndexerND<dim> indexer(box);
793 IndexTypeND<dim> ixtype = box.ixType();
794 constexpr int nitems_per_thread = 4;
795 Long nblocks_ec = (box.numPts() + nitems_per_thread*AMREX_GPU_MAX_THREADS-1)
796 / (nitems_per_thread*AMREX_GPU_MAX_THREADS);
797 nblocks_ec = std::min<Long>(nblocks_ec, reduce_data.maxBlocks());
798 reduce_data.updateMaxStreamIndex(stream);
799#ifdef AMREX_USE_SYCL
800 // device reduce needs local(i.e., shared) memory
801 constexpr std::size_t shared_mem_bytes = sizeof(ReduceTuple)
802 * std::max(Gpu::Device::warp_size, AMREX_GPU_MAX_THREADS/Gpu::Device::warp_size);
803 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, shared_mem_bytes, stream,
804 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
805 {
806 Dim1 blockIdx {gh.blockIdx()};
807 Dim1 threadIdx{gh.threadIdx()};
808 Dim1 gridDim {gh.gridDim()};
809#else
810 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, 0, stream,
811 [=] AMREX_GPU_DEVICE () noexcept
812 {
813#endif
814 ReduceTuple r;
815 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
816 ReduceTuple& dst = *(dp+blockIdx.x);
817 if (threadIdx.x == 0 && static_cast<int>(blockIdx.x) >= nblocks) {
818 dst = r;
819 }
820 for (std::uint64_t icell = std::uint64_t(AMREX_GPU_MAX_THREADS)*blockIdx.x+threadIdx.x,
821 stride = std::uint64_t(AMREX_GPU_MAX_THREADS)*gridDim.x;
822 icell < indexer.numPts();
823 icell += stride)
824 {
825 auto iv = indexer.intVect(icell);
826 amrex::ignore_unused(f,ncomp,ixtype); // work around first-capture
827 if constexpr (std::is_same_v<Reduce::detail::iterate_box,Tag>) {
828 auto pr = Reduce::detail::call_f_intvect_box(f, iv, ixtype);
829 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, pr);
830 } else {
831 for (int n = 0; n < ncomp; ++n) {
832 auto pr = Reduce::detail::call_f_intvect_n(f, iv, n);
833 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, pr);
834 }
835 }
836 }
837#ifdef AMREX_USE_SYCL
838 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r, gh);
839#else
840 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r);
841#endif
842 });
843 nblocks = std::max(nblocks, static_cast<int>(nblocks_ec));
844 }
845
847
848 template <FabArrayType MF, typename D, typename F>
849#ifndef AMREX_USE_CUDA
851#endif
852 void eval (MF const& mf, IntVect const& nghost, D& reduce_data, F&& f)
853 {
854 using ReduceTuple = typename D::Type;
855 const int nboxes = mf.local_size();
856 if (nboxes == 0) {
857 return;
858 } else if (!mf.isFusingCandidate()) {
859 for (MFIter mfi(mf); mfi.isValid(); ++mfi) {
860 Box const& b = amrex::grow(mfi.validbox(), nghost);
861 const int li = mfi.LocalIndex();
862 this->eval(b, reduce_data,
863 [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept -> ReduceTuple
864 {
865 return f(li, i, j, k);
866 });
867 }
868 } else {
869 eval_mf(Reduce::detail::iterate_box{},
870 mf, nghost, 0, reduce_data, std::forward<F>(f));
871 }
872 }
873
874 template <FabArrayType MF, typename D, typename F>
875#ifndef AMREX_USE_CUDA
877#endif
878 void eval (MF const& mf, IntVect const& nghost, int ncomp, D& reduce_data, F&& f)
879 {
880 using ReduceTuple = typename D::Type;
881
882 const int nboxes = mf.local_size();
883
884 if (nboxes == 0) {
885 return;
886 } else if (!mf.isFusingCandidate()) {
887 for (MFIter mfi(mf); mfi.isValid(); ++mfi) {
888 Box const& b = amrex::grow(mfi.validbox(), nghost);
889 const int li = mfi.LocalIndex();
890 this->eval(b, ncomp, reduce_data,
891 [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept -> ReduceTuple
892 {
893 return f(li, i, j, k, n);
894 });
895 }
896 } else if (ncomp == 1) {
897 // Without the loop over components, the kernel reads memory faster.
898 eval_mf(Reduce::detail::iterate_box{}, mf, nghost, 0, reduce_data,
899 [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k) noexcept -> ReduceTuple
900 {
901 return f(box_no, i, j, k, 0);
902 });
903 } else {
904 eval_mf(Reduce::detail::iterate_box_comp{},
905 mf, nghost, ncomp, reduce_data, std::forward<F>(f));
906 }
907 }
908
909 template <typename D, typename F, int dim>
910 void eval (BoxND<dim> const& box, D & reduce_data, F const& f)
911 {
912 eval_box(Reduce::detail::iterate_box{}, box, 0, reduce_data, f);
913 }
914
915 template <std::integral N, typename D, typename F, int dim>
916 void eval (BoxND<dim> const& box, N ncomp, D & reduce_data, F const& f)
917 {
918 if (ncomp == 1) {
919 // Without the loop over components, the kernel reads memory faster.
920 eval_box(Reduce::detail::iterate_box{}, box, 0, reduce_data,
921 [=] AMREX_GPU_DEVICE (IntVectND<dim> const& iv) noexcept
922 {
923 return Reduce::detail::call_f_intvect_n(f, iv, 0);
924 });
925 } else {
926 eval_box(Reduce::detail::iterate_box_comp{}, box, ncomp, reduce_data, f);
927 }
928 }
929
930 template <std::integral N, typename D, typename F>
931 void eval (N n, D & reduce_data, F const& f)
932 {
933 if (n <= 0) { return; }
934 using ReduceTuple = typename D::Type;
935 auto const& stream = Gpu::gpuStream();
936 auto dp = reduce_data.devicePtr(stream);
937 int& nblocks = reduce_data.nBlocks(stream);
938 constexpr int nitems_per_thread = 4;
939 Long nblocks_ec_long = (Long(n) + nitems_per_thread*AMREX_GPU_MAX_THREADS-1)
940 / (nitems_per_thread*AMREX_GPU_MAX_THREADS);
941 int nblocks_ec = static_cast<int>(std::min<Long>(nblocks_ec_long,
942 reduce_data.maxBlocks()));
943 reduce_data.updateMaxStreamIndex(stream);
944#ifdef AMREX_USE_SYCL
945 // device reduce needs local(i.e., shared) memory
946 constexpr std::size_t shared_mem_bytes = sizeof(ReduceTuple)
947 * std::max(Gpu::Device::warp_size, AMREX_GPU_MAX_THREADS/Gpu::Device::warp_size);
948 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, shared_mem_bytes, stream,
949 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
950 {
951 Dim1 blockIdx {gh.blockIdx()};
952 Dim1 threadIdx{gh.threadIdx()};
953 Dim1 gridDim {gh.gridDim()};
954#else
955 amrex::launch<AMREX_GPU_MAX_THREADS>(nblocks_ec, 0, stream,
956 [=] AMREX_GPU_DEVICE () noexcept
957 {
958#endif
959 ReduceTuple r;
960 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
961 ReduceTuple& dst = *(dp+blockIdx.x);
962 if (threadIdx.x == 0 && static_cast<int>(blockIdx.x) >= nblocks) {
963 dst = r;
964 }
965 for (Long i = Long(AMREX_GPU_MAX_THREADS)*blockIdx.x+threadIdx.x,
966 stride = Long(AMREX_GPU_MAX_THREADS)*gridDim.x;
967 i < Long(n);
968 i += stride)
969 {
970 auto pr = f(N(i));
971 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r,pr);
972 }
973#ifdef AMREX_USE_SYCL
974 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r, gh);
975#else
976 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r);
977#endif
978 });
979 nblocks = amrex::max(nblocks, nblocks_ec);
980 }
981
982 template <typename D>
983 typename D::Type value (D & reduce_data)
984 {
985 auto hp = reduce_data.hostPtr();
986
987 if (m_result_is_ready) {
988 reduce_data.markValueCalled();
989 return *hp;
990 }
991
992 using ReduceTuple = typename D::Type;
993 auto const& stream = Gpu::gpuStream();
994 auto dp = reduce_data.devicePtr();
995 auto const& nblocks = reduce_data.nBlocks();
996#if defined(AMREX_USE_SYCL)
997 if (reduce_data.maxStreamIndex() == 0 && nblocks[0] <= 4096) {
998 const int N = nblocks[0];
999 if (N == 0) {
1000 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(*hp);
1001 } else {
1003 Gpu::dtoh_memcpy_async(tmp.data(), dp, sizeof(ReduceTuple)*N);
1005 for (int i = 1; i < N; ++i) {
1006 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(tmp[0], tmp[i]);
1007 }
1008 *hp = tmp[0];
1009 }
1010 } else
1011#endif
1012 {
1013 int maxblocks = reduce_data.maxBlocks();
1014#ifdef AMREX_USE_SYCL
1015 // device reduce needs local(i.e., shared) memory
1016 constexpr std::size_t shared_mem_bytes = sizeof(ReduceTuple)
1017 * std::max(Gpu::Device::warp_size, AMREX_GPU_MAX_THREADS/Gpu::Device::warp_size);
1018#ifndef AMREX_NO_SYCL_REDUCE_WORKAROUND
1019 // xxxxx SYCL todo: reduce bug workaround
1021 auto presult = dtmp.data();
1022#else
1023 auto presult = hp;
1024#endif
1025 amrex::launch<AMREX_GPU_MAX_THREADS>(1, shared_mem_bytes, stream,
1026 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept
1027 {
1028 ReduceTuple r;
1029 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
1030 ReduceTuple dst = r;
1031 for (int istream = 0, nstreams = nblocks.size(); istream < nstreams; ++istream) {
1032 auto dp_stream = dp+istream*maxblocks;
1033 for (int i = gh.item->get_global_id(0), stride = gh.item->get_global_range(0);
1034 i < nblocks[istream]; i += stride) {
1035 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, dp_stream[i]);
1036 }
1037 }
1038 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r, gh);
1039 if (gh.threadIdx() == 0) { *presult = dst; }
1040 });
1041#ifndef AMREX_NO_SYCL_REDUCE_WORKAROUND
1042 Gpu::dtoh_memcpy_async(hp, dtmp.data(), sizeof(ReduceTuple));
1043#endif
1044#else
1046 [=] AMREX_GPU_DEVICE () noexcept
1047 {
1048 ReduceTuple r;
1049 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(r);
1050 ReduceTuple dst = r;
1051 for (int istream = 0, nstreams = nblocks.size(); istream < nstreams; ++istream) {
1052 auto dp_stream = dp+istream*maxblocks;
1053 for (int i = AMREX_GPU_MAX_THREADS*blockIdx.x+threadIdx.x, stride = AMREX_GPU_MAX_THREADS*gridDim.x;
1054 i < nblocks[istream]; i += stride) {
1055 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, dp_stream[i]);
1056 }
1057 }
1058 Reduce::detail::for_each_parallel<0, ReduceTuple, Ps...>(dst, r);
1059 if (threadIdx.x == 0) { *hp = dst; }
1060 });
1061#endif
1063 }
1064
1065 m_result_is_ready = true;
1066 reduce_data.markValueCalled();
1067 return *hp;
1068 }
1069
1070private:
1071 template <typename... T> friend class ReduceData;
1072 bool m_result_is_ready = false;
1073 void resetResultReadiness () { m_result_is_ready = false; }
1074};
1075
1076namespace Reduce {
1077
1078template <typename T, std::integral N>
1079T Sum (N n, T const* v, T init_val)
1080{
1081 ReduceOps<ReduceOpSum> reduce_op;
1082 ReduceData<T> reduce_data(reduce_op);
1083 using ReduceTuple = typename decltype(reduce_data)::Type;
1084 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {v[i]}; });
1085 ReduceTuple hv = reduce_data.value(reduce_op);
1086 return amrex::get<0>(hv) + init_val;
1087}
1088
1089template <typename T, std::integral N, typename F>
1090requires (!std::same_as<T*,std::decay_t<F>>)
1091T Sum (N n, F const& f, T init_val)
1092{
1093 ReduceOps<ReduceOpSum> reduce_op;
1094 ReduceData<T> reduce_data(reduce_op);
1095 using ReduceTuple = typename decltype(reduce_data)::Type;
1096 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {f(i)}; });
1097 ReduceTuple hv = reduce_data.value(reduce_op);
1098 return amrex::get<0>(hv) + init_val;
1099}
1100
1101template <typename T, std::integral N>
1102T Min (N n, T const* v, T init_val)
1103{
1104 ReduceOps<ReduceOpMin> reduce_op;
1105 ReduceData<T> reduce_data(reduce_op);
1106 using ReduceTuple = typename decltype(reduce_data)::Type;
1107 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {v[i]}; });
1108 ReduceTuple hv = reduce_data.value(reduce_op);
1109 return std::min(amrex::get<0>(hv),init_val);
1110}
1111
1112template <typename T, std::integral N, typename F>
1113requires (!std::same_as<T*,std::decay_t<F>>)
1114T Min (N n, F const& f, T init_val)
1115{
1116 ReduceOps<ReduceOpMin> reduce_op;
1117 ReduceData<T> reduce_data(reduce_op);
1118 using ReduceTuple = typename decltype(reduce_data)::Type;
1119 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {f(i)}; });
1120 ReduceTuple hv = reduce_data.value(reduce_op);
1121 return std::min(amrex::get<0>(hv),init_val);
1122}
1123
1124template <typename T, std::integral N>
1125T Max (N n, T const* v, T init_val)
1126{
1127 ReduceOps<ReduceOpMax> reduce_op;
1128 ReduceData<T> reduce_data(reduce_op);
1129 using ReduceTuple = typename decltype(reduce_data)::Type;
1130 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {v[i]}; });
1131 ReduceTuple hv = reduce_data.value(reduce_op);
1132 return std::max(amrex::get<0>(hv),init_val);
1133}
1134
1135template <typename T, std::integral N, typename F>
1136requires (!std::same_as<T*,std::decay_t<F>>)
1137T Max (N n, F const& f, T init_val)
1138{
1139 ReduceOps<ReduceOpMax> reduce_op;
1140 ReduceData<T> reduce_data(reduce_op);
1141 using ReduceTuple = typename decltype(reduce_data)::Type;
1142 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple { return {f(i)}; });
1143 ReduceTuple hv = reduce_data.value(reduce_op);
1144 return std::max(amrex::get<0>(hv),init_val);
1145}
1146
1147template <typename T, std::integral N>
1148std::pair<T,T> MinMax (N n, T const* v)
1149{
1151 ReduceData<T,T> reduce_data(reduce_op);
1152 using ReduceTuple = typename decltype(reduce_data)::Type;
1153 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple {
1154 return {v[i],v[i]};
1155 });
1156 auto hv = reduce_data.value(reduce_op);
1157 return std::make_pair(amrex::get<0>(hv), amrex::get<1>(hv));
1158}
1159
1160template <typename T, std::integral N, typename F>
1161requires (!std::same_as<T*,std::decay_t<F>>)
1162std::pair<T,T> MinMax (N n, F const& f)
1163{
1165 ReduceData<T,T> reduce_data(reduce_op);
1166 using ReduceTuple = typename decltype(reduce_data)::Type;
1167 reduce_op.eval(n, reduce_data, [=] AMREX_GPU_DEVICE (N i) -> ReduceTuple {
1168 T tmp = f(i);
1169 return {tmp,tmp};
1170 });
1171 auto hv = reduce_data.value(reduce_op);
1172 return std::make_pair(amrex::get<0>(hv), amrex::get<1>(hv));
1173}
1174
1175template <typename T, std::integral N, typename P>
1176bool AnyOf (N n, T const* v, P const& pred)
1177{
1178 Gpu::LaunchSafeGuard lsg(true);
1180 int* dp = ds.dataPtr();
1181 auto ec = Gpu::ExecutionConfig(n);
1182 ec.numBlocks.x = std::min(ec.numBlocks.x, Gpu::Device::maxBlocksPerLaunch());
1183
1184#ifdef AMREX_USE_SYCL
1185 const int num_ints = std::max(Gpu::Device::warp_size, int(ec.numThreads.x)/Gpu::Device::warp_size) + 1;
1186 const std::size_t shared_mem_bytes = num_ints*sizeof(int);
1187 amrex::launch<AMREX_GPU_MAX_THREADS>(ec.numBlocks.x, shared_mem_bytes, Gpu::gpuStream(),
1188 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept {
1189 int* has_any = &(static_cast<int*>(gh.sharedMemory())[num_ints-1]);
1190 if (gh.threadIdx() == 0) { *has_any = *dp; }
1191 gh.syncThreads();
1192
1193 if (!(*has_any))
1194 {
1195 int r = false;
1196 for (Long i = Long(AMREX_GPU_MAX_THREADS)*gh.blockIdx()+gh.threadIdx(),
1197 stride = Long(AMREX_GPU_MAX_THREADS)*gh.gridDim();
1198 i < Long(n) && !r; i += stride)
1199 {
1200 r = pred(v[i]) ? 1 : 0;
1201 }
1202
1205 if (gh.threadIdx() == 0 && r) { *dp = 1; }
1206 }
1207 });
1208#else
1210 [=] AMREX_GPU_DEVICE () noexcept {
1211 __shared__ int has_any;
1212 if (threadIdx.x == 0) { has_any = *dp; }
1213 __syncthreads();
1214
1215 if (!has_any)
1216 {
1217 int r = false;
1218 for (Long i = Long(AMREX_GPU_MAX_THREADS)*blockIdx.x+threadIdx.x,
1219 stride = Long(AMREX_GPU_MAX_THREADS)*gridDim.x;
1220 i < Long(n) && !r; i += stride)
1221 {
1222 r = pred(v[i]) ? 1 : 0;
1223 }
1226 if (threadIdx.x == 0 && r) *dp = 1;
1227 }
1228 });
1229#endif
1230 return ds.dataValue();
1231}
1232
1233template <typename P, int dim>
1234bool AnyOf (BoxND<dim> const& box, P const& pred)
1235{
1236 Gpu::LaunchSafeGuard lsg(true);
1238 int* dp = ds.dataPtr();
1239 const BoxIndexerND<dim> indexer(box);
1240 auto ec = Gpu::ExecutionConfig(box.numPts());
1241 ec.numBlocks.x = std::min(ec.numBlocks.x, Gpu::Device::maxBlocksPerLaunch());
1242
1243#ifdef AMREX_USE_SYCL
1244 const int num_ints = std::max(Gpu::Device::warp_size, int(ec.numThreads.x)/Gpu::Device::warp_size) + 1;
1245 const std::size_t shared_mem_bytes = num_ints*sizeof(int);
1246 amrex::launch<AMREX_GPU_MAX_THREADS>(ec.numBlocks.x, shared_mem_bytes, Gpu::gpuStream(),
1247 [=] AMREX_GPU_DEVICE (Gpu::Handler const& gh) noexcept {
1248 int* has_any = &(static_cast<int*>(gh.sharedMemory())[num_ints-1]);
1249 if (gh.threadIdx() == 0) { *has_any = *dp; }
1250 gh.syncThreads();
1251
1252 if (!(*has_any))
1253 {
1254 int r = false;
1255 for (std::uint64_t icell = std::uint64_t(AMREX_GPU_MAX_THREADS)*gh.blockIdx()+gh.threadIdx(),
1256 stride = std::uint64_t(AMREX_GPU_MAX_THREADS)*gh.gridDim();
1257 icell < indexer.numPts() && !r;
1258 icell += stride)
1259 {
1260 auto iv = indexer.intVect(icell);
1261 r = amrex::detail::call_f_intvect(pred, iv) ? 1 : 0;
1262 }
1265 if (gh.threadIdx() == 0 && r) { *dp = 1; }
1266 }
1267 });
1268#else
1269 AMREX_LAUNCH_KERNEL(AMREX_GPU_MAX_THREADS, ec.numBlocks, ec.numThreads, 0,
1271 [=] AMREX_GPU_DEVICE () noexcept {
1272 __shared__ int has_any;
1273 if (threadIdx.x == 0) { has_any = *dp; }
1274 __syncthreads();
1275
1276 if (!has_any)
1277 {
1278 int r = false;
1279 for (std::uint64_t icell = std::uint64_t(AMREX_GPU_MAX_THREADS)*blockIdx.x+threadIdx.x,
1280 stride = std::uint64_t(AMREX_GPU_MAX_THREADS)*gridDim.x;
1281 icell < indexer.numPts() && !r;
1282 icell += stride)
1283 {
1284 auto iv = indexer.intVect(icell);
1285 r = amrex::detail::call_f_intvect(pred, iv) ? 1 : 0;
1286 }
1289 if (threadIdx.x == 0 && r) *dp = 1;
1290 }
1291 });
1292#endif
1293 return ds.dataValue();
1294}
1295
1296}
1297
1298#else
1299
1300template <typename... Ts>
1301class ReduceData
1302{
1303public:
1304 using Type = GpuTuple<Ts...>;
1305
1306 template <typename... Ps>
1307 explicit ReduceData (ReduceOps<Ps...>& reduce_op)
1308 : m_tuple(OpenMP::in_parallel() ? 1 : OpenMP::get_max_threads()),
1309 m_fn_value([&reduce_op,this] () -> Type { return this->value(reduce_op); })
1310 {
1311 reduce_op.resetResultReadiness();
1312 for (auto& t : m_tuple) {
1313 Reduce::detail::for_each_init<0, Type, Ps...>(t);
1314 }
1315 }
1316
1317 ~ReduceData () = default;
1318 ReduceData (ReduceData<Ts...> const&) = delete;
1319 ReduceData (ReduceData<Ts...> &&) = delete;
1320 void operator= (ReduceData<Ts...> const&) = delete;
1321 void operator= (ReduceData<Ts...> &&) = delete;
1322
1323 Type value () { return m_fn_value(); }
1324
1325 template <typename... Ps>
1326 Type value (ReduceOps<Ps...>& reduce_op)
1327 {
1328 return reduce_op.value(*this);
1329 }
1330
1331 Vector<Type>& reference () { return m_tuple; }
1332
1333 Type& reference (int tid)
1334 {
1335 if (m_tuple.size() == 1) {
1336 // No OpenMP or already inside OpenMP parallel when reduce_data is constructed
1337 return m_tuple[0];
1338 } else {
1339 return m_tuple[tid];
1340 }
1341 }
1342
1343private:
1344 Vector<Type> m_tuple;
1345 std::function<Type()> m_fn_value;
1346};
1347
1348namespace Reduce::detail {
1349
1350 // call_f_intvect
1351
1352 template <typename F, int dim>
1354 auto call_f_intvect (F const& f, IntVectND<dim> iv) noexcept ->
1355 decltype(amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv))
1356 {
1357 return amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv);
1358 }
1359
1360 // call_f_intvect_n
1361
1362 template <typename F, typename T, int dim>
1364 auto call_f_intvect_n (F const& f, IntVectND<dim> iv, T n) noexcept ->
1365 decltype(amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv, n))
1366 {
1367 return amrex::detail::call_f_intvect_inner(std::make_index_sequence<dim>(), f, iv, n);
1368 }
1369}
1370
1371template <typename... Ps>
1372class ReduceOps
1373{
1374private:
1375
1376 // call_f_box
1377
1378 template <typename D, typename F, int dim>
1379 requires (std::same_as<std::decay_t<decltype(
1380 Reduce::detail::call_f_intvect(std::declval<F const&>(), IntVectND<dim>()))>,
1381 typename D::Type>)
1383 static void call_f_box (BoxND<dim> const& box, typename D::Type & r, F const& f) noexcept
1384 {
1385 using ReduceTuple = typename D::Type;
1386 ReduceTuple rem;
1387 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rem);
1388 Reduce::detail::Lanes<ReduceTuple> acc;
1389 Reduce::detail::lanes_set<0>(acc, rem);
1390 int const ilo = box.smallEnd(0);
1391 int const ihi = box.bigEnd(0);
1392 BoxND<dim> rows = box;
1393 rows.setBig(0, ilo);
1394 For(rows, [&] (IntVectND<dim> iv) {
1395 Reduce::detail::reduce_row<Ps...>(ilo, ihi, acc, rem, [&] (int i) {
1396 iv[0] = i;
1397 return Reduce::detail::call_f_intvect(f, iv);
1398 });
1399 });
1400 Reduce::detail::lanes_fold<0, ReduceTuple, Ps...>(acc, r);
1401 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, rem);
1402 }
1403
1404 template <typename D, typename F, int dim>
1405 requires (std::same_as<std::decay_t<decltype(
1406 std::declval<F const&>()(std::declval<BoxND<dim> const&>()))>,
1407 typename D::Type>)
1409 static void call_f_box (BoxND<dim> const& box, typename D::Type & r, F const& f) noexcept
1410 {
1411 using ReduceTuple = typename D::Type;
1412 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(r, f(box));
1413 }
1414
1415public:
1416
1417 template <FabArrayType MF, typename D, typename F>
1418 requires (IsCallable<F, int, int, int, int>::value)
1419 void eval (MF const& mf, IntVect const& nghost, D & reduce_data, F const& f)
1420 {
1421 using ReduceTuple = typename D::Type;
1422#ifdef AMREX_USE_OMP
1423#pragma omp parallel if (!system::regtest_reduction)
1424#endif
1425 {
1426 // The partial accumulators break the dependency chain so that
1427 // the i loop can vectorize. They live across the MFIter loop so
1428 // that they stay in vector registers.
1429 ReduceTuple rem;
1430 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rem);
1431 Reduce::detail::Lanes<ReduceTuple> acc;
1432 Reduce::detail::lanes_set<0>(acc, rem);
1433 for (MFIter mfi(mf,true); mfi.isValid(); ++mfi) {
1434 Box const& b = mfi.growntilebox(nghost);
1435 const int li = mfi.LocalIndex();
1436 const auto lo = amrex::lbound(b);
1437 const auto hi = amrex::ubound(b);
1438 for (int k = lo.z; k <= hi.z; ++k) {
1439 for (int j = lo.y; j <= hi.y; ++j) {
1440 Reduce::detail::reduce_row<Ps...>(lo.x, hi.x, acc, rem,
1441 [&] (int i) { return f(li,i,j,k); });
1442 }}
1443 }
1444 auto& rr = reduce_data.reference(OpenMP::get_thread_num());
1445 Reduce::detail::lanes_fold<0, ReduceTuple, Ps...>(acc, rr);
1446 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rr, rem);
1447 }
1448 }
1449
1450 template <FabArrayType MF, typename D, typename F>
1451 requires (IsCallable<F, int, int, int, int, int>::value)
1452 void eval (MF const& mf, IntVect const& nghost, int ncomp, D & reduce_data, F const& f)
1453 {
1454 using ReduceTuple = typename D::Type;
1455#ifdef AMREX_USE_OMP
1456#pragma omp parallel if (!system::regtest_reduction)
1457#endif
1458 {
1459 ReduceTuple rem;
1460 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rem);
1461 Reduce::detail::Lanes<ReduceTuple> acc;
1462 Reduce::detail::lanes_set<0>(acc, rem);
1463 for (MFIter mfi(mf,true); mfi.isValid(); ++mfi) {
1464 Box const& b = mfi.growntilebox(nghost);
1465 const int li = mfi.LocalIndex();
1466 const auto lo = amrex::lbound(b);
1467 const auto hi = amrex::ubound(b);
1468 for (int n = 0; n < ncomp; ++n) {
1469 for (int k = lo.z; k <= hi.z; ++k) {
1470 for (int j = lo.y; j <= hi.y; ++j) {
1471 Reduce::detail::reduce_row<Ps...>(lo.x, hi.x, acc, rem,
1472 [&] (int i) { return f(li,i,j,k,n); });
1473 }}}
1474 }
1475 auto& rr = reduce_data.reference(OpenMP::get_thread_num());
1476 Reduce::detail::lanes_fold<0, ReduceTuple, Ps...>(acc, rr);
1477 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rr, rem);
1478 }
1479 }
1480
1481 template <typename D, typename F, int dim>
1482 void eval (BoxND<dim> const& box, D & reduce_data, F&& f)
1483 {
1484 using ReduceTuple = typename D::Type;
1485 ReduceTuple rr;
1486 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rr);
1487 call_f_box<D>(box, rr, std::forward<F>(f));
1488 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(
1489 reduce_data.reference(OpenMP::get_thread_num()), rr);
1490 }
1491
1492 template <std::integral N, typename D, typename F, int dim>
1493 void eval (BoxND<dim> const& box, N ncomp, D & reduce_data, F const& f)
1494 {
1495 using ReduceTuple = typename D::Type;
1496 ReduceTuple rem;
1497 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rem);
1498 Reduce::detail::Lanes<ReduceTuple> acc;
1499 Reduce::detail::lanes_set<0>(acc, rem);
1500 int const ilo = box.smallEnd(0);
1501 int const ihi = box.bigEnd(0);
1502 BoxND<dim> rows = box;
1503 rows.setBig(0, ilo);
1504 For(rows, ncomp, [&] (IntVectND<dim> iv, int n) {
1505 Reduce::detail::reduce_row<Ps...>(ilo, ihi, acc, rem, [&] (int i) {
1506 iv[0] = i;
1507 return Reduce::detail::call_f_intvect_n(f, iv, n);
1508 });
1509 });
1510 auto& rr = reduce_data.reference(OpenMP::get_thread_num());
1511 Reduce::detail::lanes_fold<0, ReduceTuple, Ps...>(acc, rr);
1512 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rr, rem);
1513 }
1514
1515 template <std::integral N, typename D, typename F>
1516 void eval (N n, D & reduce_data, F const& f)
1517 {
1518 using ReduceTuple = typename D::Type;
1519 ReduceTuple rem;
1520 Reduce::detail::for_each_init<0, ReduceTuple, Ps...>(rem);
1521 Reduce::detail::Lanes<ReduceTuple> acc;
1522 Reduce::detail::lanes_set<0>(acc, rem);
1523 if (n > 0) {
1524 Reduce::detail::reduce_row<Ps...>(N(0), static_cast<N>(n-1), acc, rem,
1525 [&] (N i) { return f(i); });
1526 }
1527 auto& rr = reduce_data.reference(OpenMP::get_thread_num());
1528 Reduce::detail::lanes_fold<0, ReduceTuple, Ps...>(acc, rr);
1529 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rr, rem);
1530 }
1531
1532 template <typename D>
1533 typename D::Type value (D & reduce_data)
1534 {
1535 auto& rrv = reduce_data.reference();
1536 if (! m_result_is_ready) {
1537 using ReduceTuple = typename D::Type;
1538 if (rrv.size() > 1) {
1539 for (int i = 1, N = rrv.size(); i < N; ++i) {
1540 Reduce::detail::for_each_local<0, ReduceTuple, Ps...>(rrv[0], rrv[i]);
1541 }
1542 }
1543 m_result_is_ready = true;
1544 }
1545 return rrv[0];
1546 }
1547
1548private:
1549 template <typename... T> friend class ReduceData;
1550 bool m_result_is_ready = false;
1551 void resetResultReadiness () { m_result_is_ready = false; }
1552};
1553
1554namespace Reduce {
1555
1556template <typename T, std::integral N, typename F>
1557requires (!std::same_as<T*,std::decay_t<F>>)
1558T Sum (N n, F const& f, T init_val)
1559{
1560 auto const cn = Reduce::detail::chunks_1d(n);
1561 N const chunk = cn.first;
1562 N const nchunks = cn.second;
1563 T r = init_val;
1564#ifdef AMREX_USE_OMP
1565#pragma omp parallel for reduction(+:r)
1566#endif
1567 for (N ic = 0; ic < nchunks; ++ic) {
1568 N const ib = ic*chunk;
1569 N const ie = (n - ib < chunk) ? n : ib + chunk;
1570 using Tup = GpuTuple<T>;
1571 Tup rem{};
1572 Reduce::detail::Lanes<Tup> acc;
1573 Reduce::detail::lanes_set<0>(acc, rem);
1574 Reduce::detail::reduce_row<ReduceOpSum>(ib, static_cast<N>(ie-1), acc, rem,
1575 [&] (N i) { return Tup{f(i)}; });
1576 Reduce::detail::lanes_fold<0,Tup,ReduceOpSum>(acc, rem);
1577 r += amrex::get<0>(rem);
1578 }
1579 return r;
1580}
1581
1582template <typename T, std::integral N>
1583T Sum (N n, T const* v, T init_val)
1584{
1585 return Sum(n, [=] (N i) -> T { return v[i]; }, init_val);
1586}
1587
1588template <typename T, std::integral N, typename F>
1589requires (!std::same_as<T*,std::decay_t<F>>)
1590T Min (N n, F const& f, T init_val)
1591{
1592 auto const cn = Reduce::detail::chunks_1d(n);
1593 N const chunk = cn.first;
1594 N const nchunks = cn.second;
1595 T r = init_val;
1596#ifdef AMREX_USE_OMP
1597#pragma omp parallel for reduction(min:r)
1598#endif
1599 for (N ic = 0; ic < nchunks; ++ic) {
1600 N const ib = ic*chunk;
1601 N const ie = (n - ib < chunk) ? n : ib + chunk;
1602 using Tup = GpuTuple<T>;
1603 Tup rem{init_val};
1604 Reduce::detail::Lanes<Tup> acc;
1605 Reduce::detail::lanes_set<0>(acc, rem);
1606 Reduce::detail::reduce_row<ReduceOpMin>(ib, static_cast<N>(ie-1), acc, rem,
1607 [&] (N i) { return Tup{f(i)}; });
1608 Reduce::detail::lanes_fold<0,Tup,ReduceOpMin>(acc, rem);
1609 r = std::min(r, amrex::get<0>(rem));
1610 }
1611 return r;
1612}
1613
1614template <typename T, std::integral N>
1615T Min (N n, T const* v, T init_val)
1616{
1617 return Reduce::Min(n, [=] (N i) -> T { return v[i]; }, init_val);
1618}
1619
1620template <typename T, std::integral N, typename F>
1621requires (!std::same_as<T*,std::decay_t<F>>)
1622T Max (N n, F const& f, T init_val)
1623{
1624 auto const cn = Reduce::detail::chunks_1d(n);
1625 N const chunk = cn.first;
1626 N const nchunks = cn.second;
1627 T r = init_val;
1628#ifdef AMREX_USE_OMP
1629#pragma omp parallel for reduction(max:r)
1630#endif
1631 for (N ic = 0; ic < nchunks; ++ic) {
1632 N const ib = ic*chunk;
1633 N const ie = (n - ib < chunk) ? n : ib + chunk;
1634 using Tup = GpuTuple<T>;
1635 Tup rem{init_val};
1636 Reduce::detail::Lanes<Tup> acc;
1637 Reduce::detail::lanes_set<0>(acc, rem);
1638 Reduce::detail::reduce_row<ReduceOpMax>(ib, static_cast<N>(ie-1), acc, rem,
1639 [&] (N i) { return Tup{f(i)}; });
1640 Reduce::detail::lanes_fold<0,Tup,ReduceOpMax>(acc, rem);
1641 r = std::max(r, amrex::get<0>(rem));
1642 }
1643 return r;
1644}
1645
1646template <typename T, std::integral N>
1647T Max (N n, T const* v, T init_val)
1648{
1649 return Reduce::Max(n, [=] (N i) -> T { return v[i]; }, init_val);
1650}
1651
1652template <typename T, std::integral N, typename F>
1653requires (!std::same_as<T*,std::decay_t<F>>)
1654std::pair<T,T> MinMax (N n, F const& f)
1655{
1656 auto const cn = Reduce::detail::chunks_1d(n);
1657 N const chunk = cn.first;
1658 N const nchunks = cn.second;
1659 T r_min = std::numeric_limits<T>::max();
1660 T r_max = std::numeric_limits<T>::lowest();
1661#ifdef AMREX_USE_OMP
1662#pragma omp parallel for reduction(min:r_min) reduction(max:r_max)
1663#endif
1664 for (N ic = 0; ic < nchunks; ++ic) {
1665 N const ib = ic*chunk;
1666 N const ie = (n - ib < chunk) ? n : ib + chunk;
1667 using Tup = GpuTuple<T,T>;
1668 Tup rem{std::numeric_limits<T>::max(), std::numeric_limits<T>::lowest()};
1669 Reduce::detail::Lanes<Tup> acc;
1670 Reduce::detail::lanes_set<0>(acc, rem);
1671 Reduce::detail::reduce_row<ReduceOpMin,ReduceOpMax>(ib, static_cast<N>(ie-1), acc, rem,
1672 [&] (N i) { T const tmp = f(i); return Tup{tmp,tmp}; });
1673 Reduce::detail::lanes_fold<0,Tup,ReduceOpMin,ReduceOpMax>(acc, rem);
1674 r_min = std::min(r_min, amrex::get<0>(rem));
1675 r_max = std::max(r_max, amrex::get<1>(rem));
1676 }
1677 return std::make_pair(r_min,r_max);
1678}
1679
1680template <typename T, std::integral N>
1681std::pair<T,T> MinMax (N n, T const* v)
1682{
1683 return Reduce::MinMax<T>(n, [=] (N i) -> T { return v[i]; });
1684}
1685
1686template <typename T, std::integral N, typename P>
1687bool AnyOf (N n, T const* v, P const& pred)
1688{
1689 return std::any_of(v, v+n, pred);
1690}
1691
1692template <typename P, int dim>
1693bool AnyOf (BoxND<dim> const& box, P const& pred)
1694{
1695 for (auto iv : box.iterator()) { // NOLINT(readability-use-anyofallof)
1696 if (Reduce::detail::call_f_intvect(pred, iv)) { return true; }
1697 }
1698 return false;
1699}
1700
1701}
1702
1703#endif
1704
1709template <typename... Ts, typename... Ps>
1711constexpr GpuTuple<Ts...>
1713{
1714 GpuTuple<Ts...> r{};
1715 Reduce::detail::for_each_init<0, decltype(r), Ps...>(r);
1716 return r;
1717}
1718
1723template <typename... Ts, typename... Ps>
1725constexpr GpuTuple<Ts...>
1727{
1728 GpuTuple<Ts...> r{};
1729 Reduce::detail::for_each_init<0, decltype(r), Ps...>(r);
1730 return r;
1731}
1732
1734template <typename Ops, typename Ts>
1735class ReducerImpl;
1736
1737template <typename... Ops, typename... Ts>
1738class ReducerImpl<TypeList<Ops...>, TypeList<Ts...>>
1739{
1740public:
1741 static_assert(sizeof...(Ops) > 0);
1742 static_assert(sizeof...(Ts) > 0);
1743 static_assert(sizeof...(Ops) == sizeof...(Ts));
1744
1745 ReducerImpl ()
1746 : m_reduce_data(m_reduce_op)
1747 {}
1748
1749protected:
1750 using Result_t = GpuTuple<Ts...>;
1751 ReduceOps<Ops...> m_reduce_op;
1752 ReduceData<Ts...> m_reduce_data;
1753};
1755
1820template <typename Ops, typename Ts>
1822 : public ReducerImpl<ToTypeList_t<Ops>, ToTypeList_t<Ts>>
1823{
1824 using Base = ReducerImpl<ToTypeList_t<Ops>, ToTypeList_t<Ts>>;
1825public:
1826
1827 using Result_t = typename Base::Result_t;
1828 static constexpr int size = GpuTupleSize<Result_t>::value;
1829
1830 Reducer () = default;
1831 ~Reducer () = default;
1832
1834 Reducer (Reducer const&) = delete;
1835 Reducer (Reducer &&) = delete;
1836 void operator= (Reducer const&) = delete;
1837 void operator= (Reducer &&) = delete;
1839
1853 template <typename F, int dim>
1856 void eval (BoxND<dim> const& box, F&& f)
1857 {
1858 this->m_reduce_op.eval(box, this->m_reduce_data, std::forward<F>(f));
1859 }
1860
1876 template <typename F, int dim>
1878 IsCallable<F, IntVectND<dim>, int>::value)
1879 void eval (BoxND<dim> const& box, int ncomp, F&& f)
1880 {
1881 this->m_reduce_op.eval(box, ncomp, this->m_reduce_data, std::forward<F>(f));
1882 }
1883
1903 template <FabArrayType MF, typename F>
1905 void eval (MF const& mf, IntVect const& nghost, F && f)
1906 {
1907 this->m_reduce_op.eval(mf, nghost, this->m_reduce_data, std::forward<F>(f));
1908 }
1909
1932 template <FabArrayType MF, typename F>
1934 void eval (MF const& mf, IntVect const& nghost, int ncomp, F && f)
1935 {
1936 this->m_reduce_op.eval(mf, nghost, ncomp, this->m_reduce_data, std::forward<F>(f));
1937 }
1938
1951 template <typename N, typename F>
1952 requires (IsCallable<F, N>::value)
1953 void eval (N n, F && f)
1954 {
1955 this->m_reduce_op.eval(n, this->m_reduce_data, std::forward<F>(f));
1956 }
1957
1968 [[nodiscard]] Result_t getResult ()
1969 {
1970 return this->m_reduce_data.value(this->m_reduce_op);
1971 }
1972};
1973
1974}
1975
1976#endif
Runtime initialization/finalization helpers and global diagnostics.
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.
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:846
static unsigned int maxBlocksPerLaunch() noexcept
Definition AMReX_GpuDevice.H:242
static constexpr int warp_size
Definition AMReX_GpuDevice.H:239
static bool usingExternalStream() noexcept
Definition AMReX_GpuDevice.cpp:973
Cell-Based or Node-Based Indices.
Definition AMReX_IndexType.H:36
An Integer Vector in dim-Dimensional Space.
Definition AMReX_IntVect.H:149
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
T * data() noexcept
Definition AMReX_PODVector.H:672
Definition AMReX_Reduce.H:556
~ReduceData()
Definition AMReX_Reduce.H:578
int maxStreamIndex() const
Definition AMReX_Reduce.H:618
Type value()
Definition AMReX_Reduce.H:591
void updateMaxStreamIndex(gpuStream_t const &s)
Definition AMReX_Reduce.H:619
int & nBlocks(gpuStream_t const &s)
Definition AMReX_Reduce.H:614
GpuTuple< Ts... > Type
Definition AMReX_Reduce.H:558
ReduceData(ReduceOps< Ps... > &reduce_op)
Definition AMReX_Reduce.H:561
void markValueCalled() noexcept
Definition AMReX_Reduce.H:623
Type * devicePtr(gpuStream_t const &s)
Definition AMReX_Reduce.H:607
Type value(ReduceOps< Ps... > &reduce_op)
Definition AMReX_Reduce.H:599
Type * devicePtr()
Definition AMReX_Reduce.H:606
GpuArray< int, 8 > & nBlocks()
Definition AMReX_Reduce.H:613
ReduceData(ReduceData< Ts... > const &)=delete
Type * hostPtr()
Definition AMReX_Reduce.H:611
int maxBlocks() const
Definition AMReX_Reduce.H:616
ReduceData(ReduceData< Ts... > &&)=delete
Definition AMReX_Reduce.H:715
void eval(BoxND< dim > const &box, N ncomp, D &reduce_data, F const &f)
Definition AMReX_Reduce.H:916
D::Type value(D &reduce_data)
Definition AMReX_Reduce.H:983
void eval(BoxND< dim > const &box, D &reduce_data, F const &f)
Definition AMReX_Reduce.H:910
void eval(MF const &mf, IntVect const &nghost, D &reduce_data, F &&f)
Definition AMReX_Reduce.H:852
friend class ReduceData
Definition AMReX_Reduce.H:1071
void eval(MF const &mf, IntVect const &nghost, int ncomp, D &reduce_data, F &&f)
Definition AMReX_Reduce.H:878
void eval(N n, D &reduce_data, F const &f)
Definition AMReX_Reduce.H:931
static constexpr int size
Number of reduction operators.
Definition AMReX_Reduce.H:1828
Reducer()=default
Result_t getResult()
Get the final reduction result.
Definition AMReX_Reduce.H:1968
typename Base::Result_t Result_t
Reduction result type, GpuTuple<U...>, where U... are the types in Ts.
Definition AMReX_Reduce.H:1827
~Reducer()=default
void eval(BoxND< dim > const &box, F &&f)
Reduction over a Box.
Definition AMReX_Reduce.H:1856
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:1102
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:1176
std::pair< T, T > MinMax(N n, T const *v)
Compute the minimum and maximum of an array of values.
Definition AMReX_Reduce.H:1148
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:1125
T Sum(N n, T const *v, T init_val=0)
Compute the sum of an array of values.
Definition AMReX_Reduce.H:1079
__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:1328
PODVector< T, PinnedArenaAllocator< T > > PinnedVector
A PODVector that uses the pinned memory arena.
Definition AMReX_GpuContainers.H:48
PODVector< T, ArenaAllocator< T > > DeviceVector
A PODVector that uses the standard memory Arena. Note that the memory might or might not be managed d...
Definition AMReX_GpuContainers.H:24
Arena * The_Pinned_Arena()
Definition AMReX_Arena.cpp:879
Arena * The_Arena()
Definition AMReX_Arena.cpp:839
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:54
__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
Definition AMReX_BaseFwd.H:60
__device__ T blockReduce(T x, WARPREDUCE &&warp_reduce, T x0)
Definition AMReX_GpuReduce.H:317
__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
void streamSynchronize() noexcept
Definition AMReX_GpuDevice.H:329
void dtoh_memcpy_async(void *p_h, const void *p_d, const std::size_t sz) noexcept
Definition AMReX_GpuDevice.H:454
gpuStream_t gpuStream() noexcept
Definition AMReX_GpuDevice.H:310
__device__ T blockReduceSum(T source) noexcept
Definition AMReX_GpuReduce.H:353
constexpr int get_max_threads()
Definition AMReX_OpenMP.H:36
Definition AMReX_Reduce.H:22
Definition AMReX_Amr.cpp:50
__host__ __device__ void ignore_unused(const Ts &...)
No-op helper that marks variables as intentionally unused.
Definition AMReX.H:273
@ F
Definition AMReX_Order.H:7
void For(TypeList< CTOs... > ctos, std::array< int, sizeof...(CTOs)> const &runtime_options, T N, F &&f)
Definition AMReX_CTOParallelForImpl.H:400
@ Device
Definition AMReX_GpuControl.H:65
__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:1712
void launch(T const &n, L const &f) noexcept
Definition AMReX_GpuLaunchFunctsC.H:122
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
IntVectND< 3 > IntVect
IntVect is an alias for amrex::IntVectND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:38
cudaStream_t gpuStream_t
Definition AMReX_GpuControl.H:79
BoxIndexerND< 3 > BoxIndexer
Definition AMReX_Box.H:2610
const int[]
Definition AMReX_BLProfiler.cpp:1665
__global__ void launch_global(L f0, Lambdas... fs)
Definition AMReX_GpuLaunch.H:68
__host__ __device__ constexpr int get(IntVectND< dim > const &iv) noexcept
Get the Idx-th element of IntVectND<dim>.
Definition AMReX_IntVect.H:1360
Utility that maps flattened point indices back to IntVectND coordinates.
Definition AMReX_Box.H:2496
__host__ __device__ IntVectND< dim > intVect(std::uint64_t icell) const
Convert flattened point index icell to its IntVectND coordinate.
Definition AMReX_Box.H:2519
__host__ __device__ std::uint64_t numPts() const
Return the number of points covered by the indexed box.
Definition AMReX_Box.H:2554
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:86
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:409
__host__ __device__ void local_update(T &d, T s) const noexcept
Definition AMReX_Reduce.H:430
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:433
__device__ void parallel_update(T &d, T s) const noexcept
Definition AMReX_Reduce.H:421
Definition AMReX_Reduce.H:438
__device__ void parallel_update(T &d, T s) const noexcept
Definition AMReX_Reduce.H:450
__host__ __device__ void local_update(T &d, T s) const noexcept
Definition AMReX_Reduce.H:459
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:462
Definition AMReX_Reduce.H:375
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:400
__host__ __device__ void local_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:396
__device__ void parallel_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:387
Definition AMReX_Reduce.H:341
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:366
__device__ void parallel_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:353
__host__ __device__ void local_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:362
Definition AMReX_Reduce.H:311
__device__ void parallel_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:324
__host__ __device__ void local_update(T &d, T const &s) const noexcept
Definition AMReX_Reduce.H:333
constexpr void init(T &t) const noexcept
Definition AMReX_Reduce.H:336
Struct for holding types.
Definition AMReX_TypeList.H:13