21#ifndef PECLET_CORE_PYTHON_NDARRAY_INTEROP_HPP
22#define PECLET_CORE_PYTHON_NDARRAY_INTEROP_HPP
24#include <nanobind/nanobind.h>
25#include <nanobind/ndarray.h>
29#include <Kokkos_Core.hpp>
39namespace nb = nanobind;
43template <
class MemorySpace>
45 Kokkos::SpaceAccessibility<Kokkos::HostSpace, MemorySpace>::accessible;
49template <
class MemorySpace>
51 if constexpr (is_host_space_v<MemorySpace>) {
52 return {nb::device::cpu::value, 0};
54#if defined(KOKKOS_ENABLE_HIP)
55 return {nb::device::rocm::value, Kokkos::device_id()};
57 return {nb::device::cuda::value, Kokkos::device_id()};
65 if (a.dtype() != nb::dtype<T>())
66 throw std::runtime_error(std::string(who) +
": array dtype does not match the expected scalar");
77 using T = std::remove_const_t<typename V::value_type>;
78 using Mem =
typename V::memory_space;
79 constexpr std::size_t N = V::rank;
81 std::array<std::size_t, N> shape;
82 std::array<std::int64_t, N> strides;
83 for (std::size_t i = 0; i < N; ++i) {
84 shape[i] = view.extent(i);
85 strides[i] =
static_cast<std::int64_t
>(view.stride(i));
90 auto* held =
new V(view);
91 nb::capsule owner(held, [](
void* p)
noexcept {
delete static_cast<V*
>(p); });
93 auto* data =
const_cast<T*
>(view.data());
94 if constexpr (is_host_space_v<Mem>) {
95 return nb::ndarray<nb::numpy, T>(data, N, shape.data(), owner, strides.data(), nb::dtype<T>(),
96 nb::device::cpu::value, 0);
98 auto [dev, id] = dlpack_device<Mem>();
99 return nb::ndarray<T>(data, N, shape.data(), owner, strides.data(), nb::dtype<T>(), dev,
id);
109 std::initializer_list<std::size_t> shape,
110 std::initializer_list<std::int64_t> strides) {
111 auto* held =
new std::vector<T>(std::move(v));
112 nb::capsule owner(held, [](
void* p)
noexcept {
delete static_cast<std::vector<T>*
>(p); });
113 return nb::ndarray<nb::numpy, T>(held->data(), shape, owner, strides, nb::dtype<T>(),
114 nb::device::cpu::value, 0);
126 require_dtype<T>(a,
"ndarray_to_view");
127 const std::size_t n = a.size();
129 const int dev = a.device_type();
130 if (dev == nb::device::cpu::value) {
131 Kokkos::View<const T*, Kokkos::HostSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged>> hv(
132 static_cast<const T*
>(a.data()), n);
133 Kokkos::deep_copy(d, hv);
134 }
else if constexpr (!is_host_space_v<peclet::core::MemSpace>) {
135 if (dev != dlpack_device<peclet::core::MemSpace>().first)
136 throw std::runtime_error(
137 "ndarray_to_view: array is on an incompatible device for this build");
138 Kokkos::View<const T*, peclet::core::MemSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged>> dv(
139 static_cast<const T*
>(a.data()), n);
140 Kokkos::deep_copy(d, dv);
142 throw std::runtime_error(
"ndarray_to_view: array is on an incompatible device for this build");
153 require_dtype<T>(a,
"ndarray_to_vector");
154 const std::size_t n = a.size();
155 std::vector<T> out(n);
156 const int dev = a.device_type();
157 if (dev == nb::device::cpu::value) {
159 std::memcpy(out.data(), a.data(), n *
sizeof(T));
160 }
else if constexpr (!is_host_space_v<peclet::core::MemSpace>) {
161 if (dev != dlpack_device<peclet::core::MemSpace>().first)
162 throw std::runtime_error(
163 "ndarray_to_vector: array is on an incompatible device for this build");
164 Kokkos::View<const T*, peclet::core::MemSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged>> dv(
165 static_cast<const T*
>(a.data()), n);
166 Kokkos::View<T*, Kokkos::HostSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged>> hv(out.data(), n);
167 Kokkos::deep_copy(hv, dv);
169 throw std::runtime_error(
170 "ndarray_to_vector: array is on an incompatible device for this build");
nb::ndarray< nb::numpy, T > vector_to_ndarray(std::vector< T > &&v, std::initializer_list< std::size_t > shape, std::initializer_list< std::int64_t > strides)
Export a host std::vector<T> as a NumPy array of the given logical shape and element-unit strides,...
peclet::core::View< T > ndarray_to_view(const nb::ndarray<> &a, const std::string &label)
Import a (contiguous) Python array into a freshly-allocated flat device peclet::core::View<T>.
auto view_to_ndarray(const V &view)
Export a Kokkos View to a Python array without copying.
std::vector< T > ndarray_to_vector(const nb::ndarray<> &a)
Import a (contiguous) Python array into a host std::vector<T>.
void require_dtype(const nb::ndarray<> &a, const char *who)
Throw if a's element type is not T.
constexpr bool is_host_space_v
True when MemorySpace is reachable from the host (NumPy export path); false for a device-resident spa...
std::pair< int, int > dlpack_device()
Map a Kokkos memory space to a DLPack {device_type, device_id} pair.
Kokkos::View< T *, MemSpace > View
1D device array.