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) {
105 auto constexpr is_row_major = Order == Utils::MemoryOrder::ROW_MAJOR;
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) {
113 projector(index_slow, nx);
114 for (ny = start[index_medium]; ny < stop[index_medium]; ++ny) {
115 projector(index_medium, ny);
116 for (nz = start[index_fast]; nz < stop[index_fast]; ++nz) {
117 projector(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 */
135template <Utils::MemoryOrder memory_order, class Kernel>
136void for_each_3d_lin(detail::IndexVectorConcept auto &&start,
137 detail::IndexVectorConcept auto &&stop, Kernel &&kernel) {
138 if (Kokkos::num_threads() > 1) {
139 auto const size = stop - start;
140 constexpr Kokkos::Iterate iter = LayoutIterate<memory_order>::value;
141 using Range3d = Kokkos::MDRangePolicy<Kokkos::Rank<3, iter, iter>>;
142 Range3d policy({0, 0, 0}, {size[0], size[1], size[2]});
143 Kokkos::parallel_for(
144 "for_each_3d", policy, KOKKOS_LAMBDA(int i, int j, int k) {
145 auto const linear_idx =
146 Utils::get_linear_index<memory_order>(i, j, k, size);
147 kernel(start + Utils::Vector3i{{i, j, k}}, linear_idx);
148 });
149 return;
150 }
151
152 int linear_loop_index = 0u;
153 if constexpr (memory_order == Utils::MemoryOrder::ROW_MAJOR) {
154 for (int nx = start[0u]; nx < stop[0u]; ++nx) {
155 for (int ny = start[1u]; ny < stop[1u]; ++ny) {
156 for (int nz = start[2u]; nz < stop[2u]; ++nz) {
157 kernel(Utils::Vector3i{nx, ny, nz}, linear_loop_index);
158 linear_loop_index++;
159 }
160 }
161 }
162 } else {
163 for (int nz = start[2u]; nz < stop[2u]; ++nz) {
164 for (int ny = start[1u]; ny < stop[1u]; ++ny) {
165 for (int nx = start[0u]; nx < stop[0u]; ++nx) {
166 kernel(Utils::Vector3i{nx, ny, nz}, linear_loop_index);
167 linear_loop_index++;
168 }
169 }
170 }
171 }
172}
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.
void for_each_3d_lin(detail::IndexVectorConcept auto &&start, detail::IndexVectorConcept auto &&stop, Kernel &&kernel)
Run a kernel(index_3d, linear_index) over the given 3d range with given memory order.
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:32
std::vector< T, allocator< T > > vector
Definition vector.hpp:52