ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
for_each_3d.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2024-2026 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include <config/config.hpp>
23
24#include <utils/index.hpp>
25
26#include <Kokkos_Core.hpp>
27
28#include <concepts>
29#include <cstddef>
30#include <type_traits>
31
32namespace detail {
33
34constexpr inline void noop_projector(unsigned, int) {}
35
36template <typename T>
37concept IndexVectorConcept = requires(T vector) {
38 { vector[0] } -> std::convertible_to<std::size_t>;
39};
40
41} // namespace detail
42
43/**
44 * @brief Repeat an operation on every element of a 3D grid.
45 *
46 * Intermediate values that depend on the iterated coordinates
47 * are calculated and stored once per iteration. This is useful
48 * when the operation is costly.
49 *
50 * @param start Initial values for the loop counters.
51 * @param stop Final values (one-past-the-end) for the loop counters.
52 * @param counters Loop counters.
53 * @param kernel Functor to execute.
54 * @param projector Projection of the current loop counter.
55 * @tparam Kernel Nullary function.
56 * @tparam Projector Binary function that takes a nesting depth and a loop
57 * counter as arguments and projects a value.
58 */
59template <class Kernel, class Projector = decltype(detail::noop_projector)>
60 requires std::invocable<Kernel> and std::invocable<Projector, unsigned, int>
61void for_each_3d(detail::IndexVectorConcept auto &&start,
62 detail::IndexVectorConcept auto &&stop,
63 detail::IndexVectorConcept auto &&counters, Kernel &&kernel,
64 Projector &&projector = detail::noop_projector) {
65 auto &nx = counters[0u];
66 auto &ny = counters[1u];
67 auto &nz = counters[2u];
68 for (nx = start[0u]; nx < stop[0u]; ++nx) {
69 projector(0u, nx);
70 for (ny = start[1u]; ny < stop[1u]; ++ny) {
71 projector(1u, ny);
72 for (nz = start[2u]; nz < stop[2u]; ++nz) {
73 projector(2u, nz);
74 kernel();
75 }
76 }
77 }
78}
79
80/**
81 * @brief Repeat an operation on every element of a 3D grid.
82 *
83 * Intermediate values that depend on the iterated coordinates
84 * are calculated and stored once per iteration. This is useful
85 * when the operation is costly.
86 *
87 * @param start Initial values for the loop counters.
88 * @param stop Final values (one-past-the-end) for the loop counters.
89 * @param counters Loop counters.
90 * @param kernel Functor to execute.
91 * @param projector Projection of the current loop counter.
92 * @tparam Order Data layout.
93 * @tparam Kernel Nullary function.
94 * @tparam Projector Binary function that takes a nesting depth and a loop
95 * counter as arguments and projects a value.
96 */
97template <Utils::MemoryOrder Order, class Kernel,
98 class Projector = decltype(detail::noop_projector)>
99 requires std::invocable<Kernel> and std::invocable<Projector, unsigned, int>
100void for_each_3d_order(detail::IndexVectorConcept auto &&start,
101 detail::IndexVectorConcept auto &&stop,
102 detail::IndexVectorConcept auto &&counters,
103 Kernel &&kernel,
104 Projector &&projector = detail::noop_projector) {
106 auto constexpr index_fast = is_row_major ? 2u : 0u;
107 auto constexpr index_slow = is_row_major ? 0u : 2u;
108 auto constexpr index_medium = 1u;
109 auto &nx = counters[index_slow];
110 auto &ny = counters[index_medium];
111 auto &nz = counters[index_fast];
112 for (nx = start[index_slow]; nx < stop[index_slow]; ++nx) {
114 for (ny = start[index_medium]; ny < stop[index_medium]; ++ny) {
116 for (nz = start[index_fast]; nz < stop[index_fast]; ++nz) {
118 kernel();
119 }
120 }
121 }
122}
123
124/** @brief Mapping between ESPResSo and Kokkos tags for memory order */
125template <Utils::MemoryOrder Order>
126using LayoutIterate = std::conditional_t<
128 std::integral_constant<Kokkos::Iterate, Kokkos::Iterate::Left>,
129 std::integral_constant<Kokkos::Iterate, Kokkos::Iterate::Right>>;
130
131/**
132 * @brief Run a kernel(index_3d, linear_index) over the given 3d range with
133 * given memory order
134 */
136 class ExecSpace = Kokkos::DefaultHostExecutionSpace>
137void for_each_3d_lin(detail::IndexVectorConcept auto &&start,
138 detail::IndexVectorConcept auto &&stop, auto &&kernel)
139 requires Kokkos::is_execution_space<ExecSpace>::value
140{
141 auto constexpr is_host_exec_space =
142 Kokkos::SpaceAccessibility<ExecSpace, Kokkos::HostSpace>::accessible != 0;
143 if constexpr (is_host_exec_space) {
144 if (Kokkos::num_threads() == 1) {
145 // serial execution policy is only implemented for host code
146 int linear_loop_index = 0u;
148 for (int nx = start[0u]; nx < stop[0u]; ++nx) {
149 for (int ny = start[1u]; ny < stop[1u]; ++ny) {
150 for (int nz = start[2u]; nz < stop[2u]; ++nz) {
153 }
154 }
155 }
156 } else {
157 for (int nz = start[2u]; nz < stop[2u]; ++nz) {
158 for (int ny = start[1u]; ny < stop[1u]; ++ny) {
159 for (int nx = start[0u]; nx < stop[0u]; ++nx) {
162 }
163 }
164 }
165 }
166 return;
167 }
168 }
169 // parallel execution policy
170 auto const size = stop - start;
171 constexpr Kokkos::Iterate iter = LayoutIterate<memory_order>::value;
172 using Range3d = Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<3, iter, iter>>;
173 Range3d policy({0, 0, 0}, {size[0], size[1], size[2]});
174 Kokkos::parallel_for("for_each_3d", policy, [=](int i, int j, int k) {
175 auto const linear_idx =
176 Utils::get_linear_index<memory_order>(i, j, k, size);
177 kernel(start + Utils::Vector3i{{i, j, k}}, linear_idx);
178 });
179}
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
and std::invocable< Projector, unsigned, int > void for_each_3d_order(detail::IndexVectorConcept auto &&start, detail::IndexVectorConcept auto &&stop, detail::IndexVectorConcept auto &&counters, Kernel &&kernel, Projector &&projector=detail::noop_projector)
Repeat an operation on every element of a 3D grid.
std::conditional_t< Order==Utils::MemoryOrder::COLUMN_MAJOR, std::integral_constant< Kokkos::Iterate, Kokkos::Iterate::Left >, std::integral_constant< Kokkos::Iterate, Kokkos::Iterate::Right > > LayoutIterate
Mapping between ESPResSo and Kokkos tags for memory order.
and std::invocable< Projector, unsigned, int > void for_each_3d(detail::IndexVectorConcept auto &&start, detail::IndexVectorConcept auto &&stop, detail::IndexVectorConcept auto &&counters, Kernel &&kernel, Projector &&projector=detail::noop_projector)
Repeat an operation on every element of a 3D grid.
MemoryOrder
Definition index.hpp:33
std::vector< T, allocator< T > > vector
Definition vector.hpp:52