pyAMReX
ArrayOfStructs.H
Go to the documentation of this file.
1 /* Copyright 2022 The AMReX Community
2  *
3  * Authors: Ryan Sandberg, Axel Huebl
4  * License: BSD-3-Clause-LBNL
5  */
6 #pragma once
7 
8 #include "pyAMReX.H"
9 
10 #include <AMReX_ArrayOfStructs.H>
11 #include <AMReX_GpuAllocators.H>
12 
13 #include <sstream>
14 
15 
16 namespace
17 {
18  using namespace amrex;
19 
24  template <typename T_ParticleType,
25  template<class> class Allocator=DefaultAllocator>
26  py::dict
28  {
29  using ParticleType = T_ParticleType;
30  using RealType = typename ParticleType::RealType;
31 
32  auto d = py::dict();
33  bool const read_only = false;
34  d["data"] = py::make_tuple(std::intptr_t(aos.dataPtr()), read_only);
35  d["shape"] = py::make_tuple(aos.size());
36  d["strides"] = py::make_tuple(sizeof(ParticleType));
37  d["typestr"] = "|V" + std::to_string(sizeof(ParticleType));
38  py::list descr;
39  descr.append(py::make_tuple("x", py::format_descriptor<RealType>::format()));
40 #if (AMREX_SPACEDIM >= 2)
41  descr.append(py::make_tuple("y", py::format_descriptor<RealType>::format()));
42 #endif
43 #if (AMREX_SPACEDIM >= 3)
44  descr.append(py::make_tuple("z", py::format_descriptor<RealType>::format()));
45 #endif
46  if constexpr (ParticleType::NReal > 0) {
47  for(int ii=0; ii < ParticleType::NReal; ii++) {
48  descr.append(py::make_tuple("rdata_"+std::to_string(ii),py::format_descriptor<RealType>::format()));
49  }
50  }
51  descr.append(py::make_tuple("idcpu", py::format_descriptor<uint64_t>::format()) );
52  if constexpr (ParticleType::NInt > 0) {
53  for(int ii=0; ii < ParticleType::NInt; ++ii) {
54  descr.append(py::make_tuple("idata_"+std::to_string(ii),py::format_descriptor<int>::format()));
55  }
56  }
57 
58  d["descr"] = descr;
59  d["version"] = 3;
60  return d;
61  }
62 }
63 
64 template <typename T_ParticleType,
65  template<class> class Allocator=DefaultAllocator>
66 void make_ArrayOfStructs(py::module &m, std::string allocstr)
67 {
68  using namespace amrex;
69 
71  using ParticleType = T_ParticleType;
72 
73  auto const aos_name = std::string("ArrayOfStructs_")
74  .append(std::to_string(ParticleType::NReal)).append("_")
75  .append(std::to_string(ParticleType::NInt)).append("_")
76  .append(allocstr);
77  py::class_<AOSType>(m, aos_name.c_str())
78  .def(py::init())
79  // TODO:
80  //operator()
81  // .def("__call__", [](AOSType const & pv){ return pv();})
82  .def("size", &AOSType::size)
83  .def("numParticles", &AOSType::numParticles)
84  .def("numRealParticles", &AOSType::numRealParticles)
85  .def("numNeighborParticles", &AOSType::numNeighborParticles)
86  .def("numTotalParticles", &AOSType::numTotalParticles)
87  .def("setNumNeighbors", &AOSType::setNumNeighbors)
88  .def("getNumNeighbors", &AOSType::getNumNeighbors)
89  .def("empty", py::overload_cast<>(&AOSType::empty))
90  .def("empty", py::overload_cast<>(&AOSType::empty, py::const_))
91  .def("push_back", &AOSType::push_back)
92  .def("pop_back", &AOSType::pop_back)
93  .def("back", py::overload_cast<>(&AOSType::back),"get back member. Problem!!!!! this is perfo")
94 
95  // setter & getter
96  .def_property_readonly("__array_interface__", [](AOSType const & aos) {
97  return array_interface(aos);
98  })
99  .def_property_readonly("__cuda_array_interface__", [](AOSType const & aos) {
100  // Nvidia GPUs: __cuda_array_interface__ v3
101  // https://numba.readthedocs.io/en/latest/cuda/cuda_array_interface.html
102  auto d = array_interface(aos);
103 
104  // data:
105  // Because the user of the interface may or may not be in the same context, the most common case is to use cuPointerGetAttribute with CU_POINTER_ATTRIBUTE_DEVICE_POINTER in the CUDA driver API (or the equivalent CUDA Runtime API) to retrieve a device pointer that is usable in the currently active context.
106  // TODO For zero-size arrays, use 0 here.
107 
108  // None or integer
109  // An optional stream upon which synchronization must take place at the point of consumption, either by synchronizing on the stream or enqueuing operations on the data on the given stream. Integer values in this entry are as follows:
110  // 0: This is disallowed as it would be ambiguous between None and the default stream, and also between the legacy and per-thread default streams. Any use case where 0 might be given should either use None, 1, or 2 instead for clarity.
111  // 1: The legacy default stream.
112  // 2: The per-thread default stream.
113  // Any other integer: a cudaStream_t represented as a Python integer.
114  // When None, no synchronization is required.
115  d["stream"] = py::none();
116 
117  d["version"] = 3;
118  return d;
119  })
120  .def("test_sizes", [](){ })
121  .def("__setitem__", [](AOSType &aos, int const v, const ParticleType& p){ aos[v] = p; })
122  .def("__getitem__", [](AOSType &aos, int const v){ return aos[v]; }, py::return_value_policy::reference)
123 
124  .def("to_host", [](AOSType const & aos) {
126  h_data.resize(aos.size());
127  //py::array_t<T_ParticleType> h_data(aos.size());
129  aos.begin(), aos.end(),
130  h_data.begin()
131  //h_data.ptr()
132  );
133  return h_data;
134  })
135  ;
136 }
137 
138 template <int NReal, int NInt>
139 void make_ArrayOfStructs(py::module &m)
140 {
141  using namespace amrex;
142 
143  // AMReX legacy AoS position + id/cpu particle ype
144  using ParticleType = Particle<NReal, NInt>;
145 
146  // first, because used as copy target in methods in containers with other allocators
147  make_ArrayOfStructs<ParticleType, amrex::PinnedArenaAllocator> (m, "pinned");
148 
149  // see Src/Base/AMReX_GpuContainers.H
150  // !AMREX_USE_GPU: DefaultAllocator = std::allocator
151  // AMREX_USE_GPU: DefaultAllocator = amrex::ArenaAllocator
152 
153  // work-around for https://github.com/pybind/pybind11/pull/4581
154  //make_ArrayOfStructs<ParticleType, std::allocator> (m, "std");
155  //make_ArrayOfStructs<ParticleType, amrex::ArenaAllocator> (m, "arena");
156 #ifdef AMREX_USE_GPU
157  make_ArrayOfStructs<ParticleType, std::allocator> (m, "std");
158  make_ArrayOfStructs<ParticleType, amrex::DefaultAllocator> (m, "default"); // amrex::ArenaAllocator
159 #else
160  make_ArrayOfStructs<ParticleType, amrex::DefaultAllocator> (m, "default"); // std::allocator
161  make_ArrayOfStructs<ParticleType, amrex::ArenaAllocator> (m, "arena");
162 #endif
163  // end work-around
164 #ifdef AMREX_USE_GPU
165  make_ArrayOfStructs<ParticleType, amrex::DeviceArenaAllocator> (m, "device");
166  make_ArrayOfStructs<ParticleType, amrex::ManagedArenaAllocator> (m, "managed");
167  make_ArrayOfStructs<ParticleType, amrex::AsyncArenaAllocator> (m, "async");
168 #endif
169 }
void make_ArrayOfStructs(py::module &m, std::string allocstr)
Definition: ArrayOfStructs.H:66
ParticleVector::iterator begin()
void resize(size_t count)
const ParticleType * dataPtr() const
std::size_t size() const
void copy(HostToDevice, InIter begin, InIter end, OutIter result) noexcept
static constexpr DeviceToHost deviceToHost
py::dict array_interface(Array4< T > const &a4)
Definition: Array4.H:62