core 1.5.0
Shared MPI block decomposition, asynchronous ghost-layer exchange, SDF geometry, load balancing (header-only C++20)
Loading...
Searching...
No Matches
host_parallel.hpp
Go to the documentation of this file.
1// core — the host-parallel shim for the shared HOST builders (setup-parallel plan D1', rung 0.5).
2//
3// The AMR setup builders (amr/momentum.hpp, amr/poisson.hpp, amr/cf_scheme.hpp,
4// amr/ghost_projection_sampled.hpp) are host-by-design: their weights are shared VERBATIM with the
5// serial host oracle (parity by construction), so they cannot move to a device kernel without
6// breaking that contract. They are also the whole of `AmrFlow::setSolid`'s cost at bed scale.
7// Rather than sprinkle OpenMP pragmas (a second parallel model, and a dead end for the later
8// device-assembly campaign), the suite's ONE parallel model — Kokkos — is used over its HOST
9// execution space. This header is the single place that dispatch lives.
10//
11// hostParFor(n, body) — body(i) for i in [0, n), any order. The caller MUST guarantee
12// the writes are DISJOINT across i (each iteration touches only
13// its own slots). Under that contract the result is bitwise
14// identical to the serial loop for ANY thread count and ANY
15// schedule — determinism by construction, never by scheduling
16// policy (plan D2).
17// hostParScan(n, body) — exclusive scan, body(i, update, final) with `update` an Index.
18// INTEGER only: an integer scan is exact regardless of the order
19// the partial sums are combined, which is what lets the two-pass
20// CSR builders assign row/slot offsets in parallel and still get
21// today's serial row order.
22//
23// Two deliberate properties:
24//
25// * Kokkos presence is detected per BUILD TREE (`__has_include(<Kokkos_Core.hpp>)`), not per
26// include order. Keying on KOKKOS_VERSION — as common/portable.hpp does for PECLET_HD, where
27// the two branches are ABI-compatible — would make this header's inline functions resolve
28// differently in TUs that reach it before vs after Kokkos_Core.hpp: an ODR violation. The
29// header pulls Kokkos in itself so every TU in a Kokkos build agrees.
30// * `Kokkos::is_initialized()` is checked at the call. Several host-oracle ctests
31// (test_amr_drag, test_amr_flow, test_amr_cf_vector, ...) link a Kokkos-enabled peclet_core but
32// never call Kokkos::initialize — they exercise the oracle, not the device. Those must keep
33// working, so an uninitialized Kokkos falls back to the serial loop instead of aborting.
34//
35// Thread count comes from the Kokkos host backend (OpenMP => OMP_NUM_THREADS). With OpenMP now in
36// the default prefix, ALWAYS pin it for test batteries: `OMP_NUM_THREADS=8 OMP_PROC_BIND=false`.
37//
38// Design: docs/amr_setup_parallel_plan.md (D1' piece 3, D2).
39#ifndef PECLET_CORE_COMMON_HOST_PARALLEL_HPP
40#define PECLET_CORE_COMMON_HOST_PARALLEL_HPP
41
42#if !defined(PECLET_CORE_HOST_PARALLEL_KOKKOS)
43#if defined(__has_include)
44#if __has_include(<Kokkos_Core.hpp>)
45#define PECLET_CORE_HOST_PARALLEL_KOKKOS 1
46#else
47#define PECLET_CORE_HOST_PARALLEL_KOKKOS 0
48#endif
49#elif defined(KOKKOS_VERSION)
50#define PECLET_CORE_HOST_PARALLEL_KOKKOS 1
51#else
52#define PECLET_CORE_HOST_PARALLEL_KOKKOS 0
53#endif
54#endif // !defined(PECLET_CORE_HOST_PARALLEL_KOKKOS)
55
56// common/view.hpp, not <Kokkos_Core.hpp> directly: several AMR headers gate their device mirrors on
57// `#ifdef KOKKOS_INLINE_FUNCTION` and, inside those blocks, use peclet::core::View / toDevice
58// without including view.hpp themselves (they rely on the includer having pulled it in first).
59// Bringing Kokkos into a TU therefore has to bring View along, or those blocks light up
60// undefined in the host-oracle TUs that reach this header.
61#if PECLET_CORE_HOST_PARALLEL_KOKKOS
63#endif
64
65#include <utility>
66
68
69namespace peclet::core {
70
73inline bool hostParallelActive() {
74#if PECLET_CORE_HOST_PARALLEL_KOKKOS
75 return Kokkos::is_initialized();
76#else
77 return false;
78#endif
79}
80
84template <class Body>
85inline void hostParFor(Index n, Body&& body) {
86 if (n <= 0)
87 return;
88#if PECLET_CORE_HOST_PARALLEL_KOKKOS
89 if (Kokkos::is_initialized()) {
90 using HostExec = Kokkos::DefaultHostExecutionSpace;
91 Kokkos::parallel_for("peclet::core::hostParFor", Kokkos::RangePolicy<HostExec>(0, n),
92 [&body](const Index i) { body(i); });
93 Kokkos::fence("peclet::core::hostParFor");
94 return;
95 }
96#endif
97 for (Index i = 0; i < n; ++i)
98 body(i);
99}
100
111template <class Body>
112inline void hostParScan(Index n, Body&& body) {
113 if (n <= 0)
114 return;
115#if PECLET_CORE_HOST_PARALLEL_KOKKOS
116 if (Kokkos::is_initialized()) {
117 using HostExec = Kokkos::DefaultHostExecutionSpace;
118 Kokkos::parallel_scan(
119 "peclet::core::hostParScan", Kokkos::RangePolicy<HostExec>(0, n),
120 [&body](const Index i, Index& update, const bool final) { body(i, update, final); });
121 Kokkos::fence("peclet::core::hostParScan");
122 return;
123 }
124#endif
125 Index update = 0;
126 for (Index i = 0; i < n; ++i)
127 body(i, update, true);
128}
129
130} // namespace peclet::core
131
132#endif // PECLET_CORE_COMMON_HOST_PARALLEL_HPP
void hostParFor(Index n, Body &&body)
Parallel for (Index i = 0; i < n; ++i) body(i); over the Kokkos HOST execution space.
void hostParScan(Index n, Body &&body)
Exclusive integer scan over the host space: body(i, update, final) where update carries the running s...
bool hostParallelActive()
Is the parallel backend actually live for this process? (Kokkos compiled in AND initialized....
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
Definition types.hpp:15