diff --git a/src/Base/CMakeLists.txt b/src/Base/CMakeLists.txt index be0f50aa..995c528c 100644 --- a/src/Base/CMakeLists.txt +++ b/src/Base/CMakeLists.txt @@ -30,6 +30,7 @@ foreach(D IN LISTS AMReX_SPACEDIM) Periodicity.cpp PlotFileUtil.cpp PODVector.cpp + TableData.cpp Utility.cpp Vector.cpp Version.cpp diff --git a/src/Base/TableData.cpp b/src/Base/TableData.cpp new file mode 100644 index 00000000..1342340e --- /dev/null +++ b/src/Base/TableData.cpp @@ -0,0 +1,131 @@ +/* Copyright 2022 The AMReX Community + * + * Authors: Axel Huebl + * License: BSD-3-Clause-LBNL + */ +#include "pyAMReX.H" + +#include + +#include + + +namespace +{ + using namespace amrex; + + /** CPU: __array_interface__ v3 + * + * https://numpy.org/doc/stable/reference/arrays.interface.html + */ + template + py::dict + array_interface(TableData const & tableData) + { + auto d = py::dict(); + bool const read_only = false; + d["data"] = py::make_tuple(std::intptr_t(tableData.table().p), read_only); + d["shape"] = py::make_tuple(tableData.size()); // TODO: ND support + d["strides"] = py::none(); // TODO: ND support + d["typestr"] = py::format_descriptor::format(); + d["version"] = 3; + return d; + } +} + +template +void make_TableData(py::module &m, std::string typestr) +{ + using namespace amrex; + + using TableData_type = TableData; + auto const td_name = std::string("TableData_") + .append(std::to_string(N)).append("D_") + .append(typestr); + + py::class_(m, td_name.c_str()) + .def("__repr__", + [typestr](TableData_type const & td) { + std::stringstream s, rs; + s << td.size(); + rs << "\n"; + return rs.str(); + } + ) + + .def(py::init<>()) + .def(py::init()) + .def(py::init, Array, Arena*>()) + + // TODO: init (non-owning) from numpy arrays / buffer protocol + // TODO: init (non-owning) from cupy arrays / cuda array protocol + // TODO: init (non-owning) from GPU arrays / dlpack protocol + + .def_property_readonly("size", &TableData_type::size) + .def_property_readonly("dim", &TableData_type::dim) + .def_property_readonly("lo", &TableData_type::lo) + .def_property_readonly("hi", &TableData_type::hi) + .def("__len__", &TableData_type::size) + + .def("copy", &TableData_type::copy) + .def("resize", &TableData_type::resize) + .def("clear", &TableData_type::clear) + + //.def("table", py::overload_cast<>(&TableData_type::table)) + //.def("const_table", &TableData_type::const_table) + + .def_property_readonly("__array_interface__", [](TableData_type const & podvector) { + return array_interface(podvector); + }) + .def_property_readonly("__cuda_array_interface__", [](TableData_type const & podvector) { + // Nvidia GPUs: __cuda_array_interface__ v3 + // https://numba.readthedocs.io/en/latest/cuda/cuda_array_interface.html + auto d = array_interface(podvector); + + // data: + // 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. + // TODO For zero-size arrays, use 0 here. + + // None or integer + // 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: + // 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. + // 1: The legacy default stream. + // 2: The per-thread default stream. + // Any other integer: a cudaStream_t represented as a Python integer. + // When None, no synchronization is required. + d["stream"] = py::none(); + + d["version"] = 3; + return d; + }) + // TODO: setter & getter + //.def("__setitem__", [](TableData_type & td, int const v, T const value){ td[v] = value; }) + //.def("__getitem__", [](TableData_type & td, int const v){ return td[v]; }) + ; +} + +template +void make_TableData(py::module &m, std::string typestr) +{ + make_TableData (m, typestr); + + // TODO: ND support + //make_TableData (m, typestr); + //make_TableData (m, typestr); + //make_TableData (m, typestr); +} + +void init_TableData(py::module& m) { + make_TableData (m, "Real"); + if constexpr(!std::is_same_v) + make_TableData (m, "ParticleReal"); + + make_TableData (m, "int"); + if constexpr(!std::is_same_v) + make_TableData (m, "Long"); + + make_TableData (m, "uint64"); +} diff --git a/src/pyAMReX.cpp b/src/pyAMReX.cpp index cd997c8f..20e83daa 100644 --- a/src/pyAMReX.cpp +++ b/src/pyAMReX.cpp @@ -35,6 +35,7 @@ void init_ParticleContainer(py::module &); void init_Periodicity(py::module &); void init_PlotFileUtil(py::module &); void init_PODVector(py::module &); +void init_TableData(py::module &); void init_Utility(py::module &); void init_Vector(py::module &); void init_Version(py::module &); @@ -78,6 +79,7 @@ PYBIND11_MODULE(amrex_3d_pybind, m) { PlotFileUtil PODVector StructOfArrays + TableData Utility Vector )pbdoc"; @@ -104,6 +106,7 @@ PYBIND11_MODULE(amrex_3d_pybind, m) { init_MultiFab(m); init_ParallelDescriptor(m); init_PODVector(m); + init_TableData(m); init_ParticleContainer(m); init_AmrMesh(m);