DDC 0.0.0

a discrete domain computation library

discrete_space.hpp
1// SPDX-License-Identifier: MIT
2
3#pragma once
4
5#include <map>
6#include <memory>
7#include <optional>
8#include <ostream>
9#include <sstream>
10#include <stdexcept>
11
12#include <Kokkos_Core.hpp>
13#if defined(__CUDACC__)
14#include <cuda.h>
15#endif
16#if defined(__HIPCC__)
17#include <hip/hip_runtime.h>
18#endif
19
20#include "ddc/discrete_domain.hpp"
21#include "ddc/discrete_space.hpp"
22#include "ddc/dual_discretization.hpp"
23
24namespace ddc {
25
26namespace detail {
27
28#if defined(__CUDACC__)
29#define CUDA_THROW_ON_ERROR(val) ddc::detail::cuda_throw_on_error((val), #val, __FILE__, __LINE__)
30template <class T>
31void cuda_throw_on_error(
32 T const err,
33 const char* const func,
34 const char* const file,
35 const int line)
36{
37 if (err != cudaSuccess) {
38 std::stringstream ss;
39 ss << "CUDA Runtime Error at: " << file << ":" << line << std::endl;
40 ss << cudaGetErrorString(err) << " " << func << std::endl;
41 throw std::runtime_error(ss.str());
42 }
43}
44#elif defined(__HIPCC__)
45#define HIP_THROW_ON_ERROR(val) ddc::detail::hip_throw_on_error((val), #val, __FILE__, __LINE__)
46template <class T>
47void hip_throw_on_error(T const err, const char* const func, const char* const file, const int line)
48{
49 if (err != hipSuccess) {
50 std::stringstream ss;
51 ss << "HIP Runtime Error at: " << file << ":" << line << std::endl;
52 ss << hipGetErrorString(err) << " " << func << std::endl;
53 throw std::runtime_error(ss.str());
54 }
55}
56#endif
57
58template <class DDim, class MemorySpace>
59using ddim_impl_t = typename DDim::template Impl<MemorySpace>;
60
61template <class T>
62class gpu_proxy
63{
64 // Here are some reasonable concepts that T should satisfy to avoid undefined behaviors:
65 // - copy-constructible: objects may be memcopied to the device,
66 // - standard layout: objects will be ensured to have the same, standard, representation on the host and the device,
67 // - trivially destructible: the destructor of objects located on a device may not be called.
68 // static_assert(std::is_standard_layout_v<T>, "Not standard layout");
69 // static_assert(std::is_trivially_destructible_v<T>, "Not trivially destructible");
70 // static_assert(std::is_trivially_copy_constructible_v<T>, "Not trivially copy-constructible");
71 // Currently not trivially destructible because for example of the Kokkos::View (mostly reference-counting)
72 // Currently not trivially copy-constructible because of discrete spaces that have deleted copy-constructors and Kokkos::View (mostly reference-counting)
73
74private:
75 alignas(T) Kokkos::Array<std::byte, sizeof(T)> m_data;
76
77public:
78 KOKKOS_FORCEINLINE_FUNCTION
79 T* operator->()
80 {
81 return reinterpret_cast<T*>(m_data.data());
82 }
83
84 KOKKOS_FORCEINLINE_FUNCTION
85 T& operator*()
86 {
87 return *reinterpret_cast<T*>(m_data.data());
88 }
89
90 KOKKOS_FORCEINLINE_FUNCTION
91 T* data()
92 {
93 return reinterpret_cast<T*>(m_data.data());
94 }
95};
96
97// Global CPU variable storing resetters. Required to correctly free data.
98inline std::optional<std::map<std::string, std::function<void()>>> g_discretization_store;
99
100// Global CPU variable owning discrete spaces data for CPU and GPU
101template <class DDim>
102inline std::optional<DualDiscretization<DDim>> g_discrete_space_dual;
103
104#if defined(__CUDACC__)
105// Global GPU variable viewing data owned by the CPU
106template <class DDim>
107__constant__ gpu_proxy<ddim_impl_t<DDim, Kokkos::CudaSpace>> g_discrete_space_device;
108#elif defined(__HIPCC__)
109// Global GPU variable viewing data owned by the CPU
110// WARNING: do not put the `inline` keyword, seems to fail on MI100 rocm/4.5.0
111template <class DDim>
112__constant__ gpu_proxy<ddim_impl_t<DDim, Kokkos::HIPSpace>> g_discrete_space_device;
113#endif
114
115inline void display_discretization_store(std::ostream& os)
116{
117 if (g_discretization_store) {
118 os << "The host discretization store is initialized:\n";
119 for (auto const& [key, value] : *g_discretization_store) {
120 os << " - " << key << "\n";
121 }
122 } else {
123 os << "The host discretization store is not initialized:\n";
124 }
125}
126
127template <class Tuple, std::size_t... Ids>
128auto extract_after(Tuple&& t, std::index_sequence<Ids...>)
129{
130 return std::make_tuple(std::move(std::get<Ids + 1>(t))...);
131}
132
133} // namespace detail
134
135/** Initialize (emplace) a global singleton discrete space
136 *
137 * @param args the constructor arguments
138 */
139template <class DDim, class... Args>
140void init_discrete_space(Args&&... args)
141{
142 if (detail::g_discrete_space_dual<DDim>) {
143 throw std::runtime_error("Discrete space function already initialized.");
144 }
145 detail::g_discrete_space_dual<DDim>.emplace(std::forward<Args>(args)...);
146 detail::g_discretization_store->emplace(typeid(DDim).name(), []() {
147 detail::g_discrete_space_dual<DDim>.reset();
148 });
149#if defined(__CUDACC__)
150 CUDA_THROW_ON_ERROR(cudaMemcpyToSymbol(
151 detail::g_discrete_space_device<DDim>,
152 &detail::g_discrete_space_dual<DDim>->get_device(),
153 sizeof(detail::g_discrete_space_dual<DDim>->get_device())));
154#elif defined(__HIPCC__)
155 HIP_THROW_ON_ERROR(hipMemcpyToSymbol(
156 detail::g_discrete_space_device<DDim>,
157 &detail::g_discrete_space_dual<DDim>->get_device(),
158 sizeof(detail::g_discrete_space_dual<DDim>->get_device())));
159#endif
160}
161
162/** Move construct a global singleton discrete space and pass through the other argument
163 *
164 * @param a - the discrete space to move at index 0
165 * - the arguments to pass through at index 1
166 */
167template <class DDimImpl, class Arg>
168Arg init_discrete_space(std::tuple<DDimImpl, Arg>&& a)
169{
170 using DDim = typename DDimImpl::discrete_dimension_type;
171 init_discrete_space<DDim>(std::move(std::get<0>(a)));
172 return std::get<1>(a);
173}
174
175/** Move construct a global singleton discrete space and pass through remaining arguments
176 *
177 * @param a - the discrete space to move at index 0
178 * - the (2+) arguments to pass through in other indices
179 */
180template <class DDimImpl, class... Args>
181std::enable_if_t<2 <= sizeof...(Args), std::tuple<Args...>> init_discrete_space(
182 std::tuple<DDimImpl, Args...>&& a)
183{
184 using DDim = typename DDimImpl::discrete_dimension_type;
185 init_discrete_space<DDim>(std::move(std::get<0>(a)));
186 return detail::extract_after(std::move(a), std::index_sequence_for<Args...>());
187}
188
189template <class DDim, class MemorySpace = DDC_CURRENT_KOKKOS_SPACE>
190KOKKOS_FORCEINLINE_FUNCTION detail::ddim_impl_t<DDim, MemorySpace> const& discrete_space()
191{
192 if constexpr (std::is_same_v<MemorySpace, Kokkos::HostSpace>) {
193 return detail::g_discrete_space_dual<DDim>->get_host();
194 }
195#if defined(__CUDACC__)
196 else if constexpr (std::is_same_v<MemorySpace, Kokkos::CudaSpace>) {
197 return *detail::g_discrete_space_device<DDim>;
198 }
199#elif defined(__HIPCC__)
200 else if constexpr (std::is_same_v<MemorySpace, Kokkos::HIPSpace>) {
201 return *detail::g_discrete_space_device<DDim>;
202 }
203#endif
204 else {
205 static_assert(std::is_same_v<MemorySpace, MemorySpace>, "Memory space not handled");
206 }
207}
208
209template <class DDim>
210detail::ddim_impl_t<DDim, Kokkos::HostSpace> const& host_discrete_space()
211{
212 return detail::g_discrete_space_dual<DDim>->get_host();
213}
214
215} // namespace ddc
Definition: aligned_allocator.hpp:9
void init_discrete_space(Args &&... args)
Initialize (emplace) a global singleton discrete space.
Definition: discrete_space.hpp:140
detail::ddim_impl_t< DDim, Kokkos::HostSpace > const & host_discrete_space()
Definition: discrete_space.hpp:210
std::enable_if_t< 2<=sizeof...(Args), std::tuple< Args... > > init_discrete_space(std::tuple< DDimImpl, Args... > &&a){ using DDim=typename DDimImpl::discrete_dimension_type;init_discrete_space< DDim >(std::move(std::get< 0 >(a)));return detail::extract_after(std::move(a), std::index_sequence_for< Args... >());}template< class DDim, class MemorySpace=DDC_CURRENT_KOKKOS_SPACE > KOKKOS_FORCEINLINE_FUNCTION detail::ddim_impl_t< DDim, MemorySpace > const & discrete_space()
Move construct a global singleton discrete space and pass through remaining arguments.
Definition: discrete_space.hpp:190
Arg init_discrete_space(std::tuple< DDimImpl, Arg > &&a)
Move construct a global singleton discrete space and pass through the other argument.
Definition: discrete_space.hpp:168