diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index afadc106d..db527802a 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -123,7 +123,8 @@ jobs: - name: Install packages run: | apt update - apt-get -y install python3-dev python3-venv python3-pybind11 ninja-build + # TODO: Use `python3-nanobind` instead of `nanobind-dev` once it is in the repo. + apt-get -y install python3-dev python3-venv nanobind-dev ninja-build - name: Clone w/ submodules uses: actions/checkout@v3 diff --git a/.github/workflows/test_pip.yml b/.github/workflows/test_pip.yml index a014f70e2..bf928f5e1 100644 --- a/.github/workflows/test_pip.yml +++ b/.github/workflows/test_pip.yml @@ -47,7 +47,7 @@ jobs: - name: Install python run: | apt update - apt-get -y install python3-dev python3-venv python3-pybind11 + apt-get -y install python3-dev python3-venv - name: Clone w/ submodules uses: actions/checkout@v3 diff --git a/README.md b/README.md index 0b8326476..c9e8e8cdb 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Documentation and instructions at [GHEX Documentation](https://ghex-org.github.i - gfortran compiler (optional, for Fortran bindings) - Python3 (optional, for Python bindings ) - MPI4PY (optional, for Python bindings ) -- Pybind11 (optional, for Python bindings) +- Nanobind (optional, for Python bindings) ### From Source diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 22def264f..7bea09d79 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -1,2 +1,2 @@ -add_subdirectory(src/_pyghex) +add_subdirectory(src/pyghex) add_subdirectory(src/ghex) diff --git a/bindings/python/src/_pyghex/py_dtype_to_cpp_name.cpp b/bindings/python/src/_pyghex/py_dtype_to_cpp_name.cpp deleted file mode 100644 index 18e1e6d0d..000000000 --- a/bindings/python/src/_pyghex/py_dtype_to_cpp_name.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ghex-org - * - * Copyright (c) 2014-2025, ETH Zurich - * All rights reserved. - * - * Please, refer to the LICENSE file in the root directory. - * SPDX-License-Identifier: BSD-3-Clause - */ -#include - -#include - -#include -#include - -#include -#include - -namespace py = pybind11; - -namespace pyghex -{ - -std::string -py_dtype_to_cpp_name(py::dtype dtype) -{ - std::string cpp_name; - - gridtools::for_each( - [&cpp_name, &dtype](auto l) - { - using type = decltype(l); - - if (dtype.is(py::dtype::of())) - { - assert(cpp_name.empty()); - cpp_name = util::mangle_python(); - } - }); - - if (cpp_name.empty()) { throw std::invalid_argument("Unsupported numpy dtype"); } - - return cpp_name; -} - -void -register_py_dtype_to_cpp_name(pybind11::module& m) -{ - m.def("py_dtype_to_cpp_name", &py_dtype_to_cpp_name, "Convert numpy dtype to C++ type name"); -} - -} // namespace pyghex diff --git a/bindings/python/src/_pyghex/structured/regular/field_descriptor.cpp b/bindings/python/src/_pyghex/structured/regular/field_descriptor.cpp deleted file mode 100644 index e38664b06..000000000 --- a/bindings/python/src/_pyghex/structured/regular/field_descriptor.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * ghex-org - * - * Copyright (c) 2014-2023, ETH Zurich - * All rights reserved. - * - * Please, refer to the LICENSE file in the root directory. - * SPDX-License-Identifier: BSD-3-Clause - */ -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include - -namespace pyghex -{ -namespace structured -{ -namespace regular -{ -namespace -{ -template -using int_tuple_constant = gridtools::meta::list...>; - -template -struct buffer_info_accessor -{ -}; - -template<> -struct buffer_info_accessor -{ - static pybind11::buffer_info get(pybind11::object& buffer) - { - return buffer.cast().request(); - } -}; - -template<> -struct buffer_info_accessor -{ - static pybind11::buffer_info get(pybind11::object& buffer) - { - using namespace pybind11::literals; -#ifdef __HIP_PLATFORM_HCC__ - pybind11::dict info = buffer.attr("__hip_array_interface__"); -#else - pybind11::dict info = buffer.attr("__cuda_array_interface__"); -#endif - - [[maybe_unused]] bool readonly = info["data"].cast()[1].cast(); - assert(!readonly); - - void* ptr = reinterpret_cast( - info["data"].cast()[0].cast()); - - // create buffer protocol format and itemsize from typestr - pybind11::function memory_view = pybind11::module::import("builtins").attr("memoryview"); - pybind11::function np_array = pybind11::module::import("numpy").attr("array"); - pybind11::buffer empty_buffer = - memory_view(np_array(pybind11::list(), "dtype"_a = info["typestr"])); - pybind11::ssize_t itemsize = empty_buffer.request().itemsize; - std::string format = empty_buffer.request().format; - - std::vector shape = info["shape"].cast>(); - pybind11::ssize_t ndim = shape.size(); - - std::vector strides(ndim); - if (pybind11::isinstance(info["strides"])) - { - strides[ndim - 1] = 1; - for (int i = ndim - 2; i >= 0; --i) - { - strides[i] = (strides[i + 1] * shape[i + 1]) * itemsize; - } - } - else - { - strides = info["strides"].cast>(); - assert(pybind11::ssize_t(strides.size()) == ndim); - } - - return pybind11::buffer_info(ptr, /* Pointer to buffer */ - itemsize, /* Size of one scalar */ - format, /* Python struct-style format descriptor */ - ndim, /* Number of dimensions */ - shape, /* Buffer dimensions */ - strides /* Strides (in bytes) for each index */ - ); - } -}; - -template -pybind11::buffer_info -get_buffer_info(pybind11::object& buffer) -{ - return buffer_info_accessor::get(buffer); -} -} // namespace - -void -register_field_descriptor(pybind11::module& m) -{ - gridtools::for_each< - gridtools::meta::transform>( - [&m](auto l) - { - using namespace std::string_literals; - using namespace pybind11::literals; - - using field_descriptor_type = gridtools::meta::first; - using T = typename field_descriptor_type::value_type; - using domain_id_type = typename field_descriptor_type::domain_id_type; - using domain_descriptor_type = typename field_descriptor_type::domain_descriptor_type; - using arch_type = typename field_descriptor_type::arch_type; - using layout_map = typename field_descriptor_type::layout_map; - using dimension = typename field_descriptor_type::dimension; - using array = std::array; - using grid_type = ghex::structured::grid::template type; - using pattern_type = ghex::pattern; - using buffer_info_type = - ghex::buffer_info; - - auto _field_descriptor = register_class(m); - /*auto _buffer_info =*/register_class(m); - - _field_descriptor.def( - pybind11::init( - [](const domain_descriptor_type& dom, pybind11::object& b, const array& offsets, - const array& extents) - { - pybind11::buffer_info info = get_buffer_info(b); - - if (!info.item_type_is_equivalent_to()) - { - std::stringstream error; - error << "Incompatible format: expected a " << typeid(T).name() - << " buffer."; - throw pybind11::type_error(error.str()); - } - - auto ordered_strides = info.strides; - std::sort(ordered_strides.begin(), ordered_strides.end(), - [](int a, int b) { return a > b; }); - array b_layout_map; - for (size_t i = 0; i < dimension::value; ++i) - { - auto it = std::find(ordered_strides.begin(), ordered_strides.end(), - info.strides[i]); - b_layout_map[i] = std::distance(ordered_strides.begin(), it); - if (b_layout_map[i] != layout_map::at(i)) - { - throw pybind11::type_error( - "Buffer has a different layout than specified."); - } - } - - return ghex::wrap_field(dom, - static_cast(info.ptr), offsets, extents, info.strides); - }), - pybind11::keep_alive<0, 2>()); - }); -} - -} // namespace regular -} // namespace structured -} // namespace pyghex diff --git a/bindings/python/src/_pyghex/unstructured/field_descriptor.cpp b/bindings/python/src/_pyghex/unstructured/field_descriptor.cpp deleted file mode 100644 index 4685aa30c..000000000 --- a/bindings/python/src/_pyghex/unstructured/field_descriptor.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* - * ghex-org - * - * Copyright (c) 2014-2023, ETH Zurich - * All rights reserved. - * - * Please, refer to the LICENSE file in the root directory. - * SPDX-License-Identifier: BSD-3-Clause - */ -#include -#include - -#include - -#include -#include -#include - -#include -#include - -namespace pyghex -{ -namespace unstructured -{ -namespace -{ - -template -struct buffer_info_accessor -{ -}; - -template<> -struct buffer_info_accessor -{ - static pybind11::buffer_info get(pybind11::object& buffer) - { - return buffer.cast().request(); - } -}; - -template<> -struct buffer_info_accessor -{ - static pybind11::buffer_info get(pybind11::object& buffer) - { - using namespace pybind11::literals; -#ifdef __HIP_PLATFORM_HCC__ - pybind11::dict info = buffer.attr("__hip_array_interface__"); -#else - pybind11::dict info = buffer.attr("__cuda_array_interface__"); -#endif - - [[maybe_unused]] bool readonly = info["data"].cast()[1].cast(); - assert(!readonly); - - void* ptr = reinterpret_cast( - info["data"].cast()[0].cast()); - - // Create buffer protocol format and itemsize from typestr - pybind11::function memory_view = pybind11::module::import("builtins").attr("memoryview"); - pybind11::function np_array = pybind11::module::import("numpy").attr("array"); - pybind11::buffer empty_buffer = - memory_view(np_array(pybind11::list(), "dtype"_a = info["typestr"])); - pybind11::ssize_t itemsize = empty_buffer.request().itemsize; - std::string format = empty_buffer.request().format; - - std::vector shape = info["shape"].cast>(); - pybind11::ssize_t ndim = shape.size(); - - std::vector strides(ndim); - if (pybind11::isinstance(info["strides"])) - { - // If `strides` field is `None` then it is contiguous C-style, - // see https://numpy.org/devdocs/reference/arrays.interface.html - strides[ndim - 1] = itemsize; - for (int i = ndim - 2; i >= 0; --i) { strides[i] = strides[i + 1] * shape[i + 1]; } - } - else - { - strides = info["strides"].cast>(); - assert(pybind11::ssize_t(strides.size()) == ndim); - } - - return pybind11::buffer_info(ptr, /* Pointer to buffer */ - itemsize, /* Size of one scalar */ - format, /* Python struct-style format descriptor */ - ndim, /* Number of dimensions */ - shape, /* Buffer dimensions */ - strides /* Strides (in bytes) for each index */ - ); - } -}; - -template -pybind11::buffer_info -get_buffer_info(pybind11::object& buffer) -{ - return buffer_info_accessor::get(buffer); -} -} // namespace - -void -register_field_descriptor(pybind11::module& m) -{ - gridtools::for_each< - gridtools::meta::transform>( - [&m](auto l) - { - using namespace std::string_literals; - using namespace pybind11::literals; - - using type = gridtools::meta::first; - using T = typename type::value_type; - using domain_id_type = typename type::domain_id_type; - using domain_descriptor_type = typename type::domain_descriptor_type; - using arch_type = typename type::arch_type; - using grid_type = ghex::unstructured::grid::template type; - using pattern_type = ghex::pattern; - using buffer_info_type = ghex::buffer_info; - - auto _field_descriptor = register_class(m); - /*auto _buffer_info = */ register_class(m); - - _field_descriptor.def( - pybind11::init( - [](const domain_descriptor_type& dom, pybind11::object& b) - { - pybind11::buffer_info info = get_buffer_info(b); - - if (!info.item_type_is_equivalent_to()) - { - std::stringstream error; - error << "Incompatible format: expected a " << typeid(T).name() - << " buffer."; - throw pybind11::type_error(error.str()); - } - - if (info.ndim > 2u) - { - std::stringstream error; - error << "Field has too many dimensions. Expected at most 2, but got " - << info.ndim; - throw pybind11::type_error(error.str()); - } - - if (static_cast(info.shape[0]) != dom.size()) - { - std::stringstream error; - error << "Field's first dimension (" - << static_cast(info.shape[0]) - << ") must match the size of the domain (" << dom.size() << ")"; - throw pybind11::type_error(error.str()); - } - - // NOTE: In `buffer_info` the strides are in bytes, but in - // GHEX they are in elements. - bool levels_first = true; - std::size_t outer_strides = 0u; - if (info.ndim == 2 && info.strides[1] != sizeof(T)) - { - levels_first = false; - if (info.strides[0] != sizeof(T)) - { - std::stringstream error; - error << "Field's strides are not compatible with GHEX. Expected " - "that the (byte) stride of dimension 0 is " - << sizeof(T) << " but got " << (std::size_t)(info.strides[0]) - << "."; - throw pybind11::type_error(error.str()); - } - if (((std::size_t)(info.strides[1]) % sizeof(T)) != 0) - { - std::stringstream error; - error << "Field's strides are not compatible with GHEX. Expected " - "that the (byte) stride of dimension 1 " - << (std::size_t)(info.strides[1]) - << " is a multiple of the element size " << sizeof(T) << "."; - throw pybind11::type_error(error.str()); - } - outer_strides = info.strides[1] / sizeof(T); - } - else if (info.ndim == 2) - { - if (info.strides[1] != sizeof(T)) - { - std::stringstream error; - error << "Field's strides are not compatible with GHEX. Expected " - "that the (byte) stride of dimension 1 is " - << sizeof(T) << " but got " << (std::size_t)(info.strides[1]) - << "."; - throw pybind11::type_error(error.str()); - } - if (((std::size_t)(info.strides[0]) % sizeof(T)) != 0) - { - std::stringstream error; - error << "Field's strides are not compatible with GHEX. Expected " - "that the (byte) stride of dimension 0 " - << (std::size_t)(info.strides[0]) - << " is a multiple of the element size of " << sizeof(T) - << "."; - throw pybind11::type_error(error.str()); - } - outer_strides = info.strides[0] / sizeof(T); - } - else - { - // Note this case only happens for `info.ndim == 1`. - if (info.strides[0] != sizeof(T)) - { - std::stringstream error; - error << "Field's strides are not compatible with GHEX. With one " - " dimension expected the stride to be " - << sizeof(T) << " but got " << info.strides[0] << "."; - throw pybind11::type_error(error.str()); - } - } - std::size_t levels = (info.ndim == 1) ? 1u : (std::size_t)info.shape[1]; - - return type{dom, static_cast(info.ptr), levels, levels_first, - outer_strides}; - }), - pybind11::keep_alive<0, 2>()); - }); -} - -} // namespace unstructured -} // namespace pyghex diff --git a/bindings/python/src/ghex/CMakeLists.txt b/bindings/python/src/ghex/CMakeLists.txt index 599c5af4b..fb1ac29ba 100644 --- a/bindings/python/src/ghex/CMakeLists.txt +++ b/bindings/python/src/ghex/CMakeLists.txt @@ -1,3 +1,5 @@ +# NOTE: These install commands only copy the `ghex` Python (wrapper) project. The actuall library, `pyghex` +# is copied in the `CMakeLists.txt` file of that folder. if (SKBUILD_PROJECT_NAME) # CMake driven by scikit-build-core install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ DESTINATION . FILES_MATCHING PATTERN "*.py") @@ -8,10 +10,13 @@ else() endif() if(GHEX_WITH_TESTING) - add_custom_target(pyghex_files) - add_custom_command(TARGET pyghex_files PRE_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/../../ghex") - add_custom_command(TARGET pyghex_files PRE_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/version.txt "${CMAKE_CURRENT_BINARY_DIR}/../../ghex") - add_dependencies(pyghex pyghex_files) + file(GLOB_RECURSE pyghex_python_files CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.py") + + add_custom_target(pyghex_files ALL + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/../../ghex" + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/version.txt "${CMAKE_CURRENT_BINARY_DIR}/../../ghex" + DEPENDS ${pyghex_python_files} ${CMAKE_BINARY_DIR}/version.txt + COMMENT "Refreshing Python binding files for tests") + + add_dependencies(pyghex_files pyghex) endif() diff --git a/bindings/python/src/ghex/__init__.py b/bindings/python/src/ghex/__init__.py index 318faebe4..a5f32a69a 100644 --- a/bindings/python/src/ghex/__init__.py +++ b/bindings/python/src/ghex/__init__.py @@ -29,7 +29,7 @@ def get_version() -> str: __version__ = get_version() -__config__ = _pyghex.config() # noqa:F405 +__config__ = pyghex.config() # noqa:F405 # Remove get_version from module. del get_version diff --git a/bindings/python/src/ghex/pyghex/__init__.py b/bindings/python/src/ghex/pyghex/__init__.py deleted file mode 100644 index 994e26367..000000000 --- a/bindings/python/src/ghex/pyghex/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# -# ghex-org -# -# Copyright (c) 2014-2023, ETH Zurich -# All rights reserved. -# -# Please, refer to the LICENSE file in the root directory. -# SPDX-License-Identifier: BSD-3-Clause -# - -# The Python wrapper generated using pybind11 is a compiled dynamic library, -# with a name like _pyghex.cpython-38-x86_64-linux-gnu.so -# -# The library will be installed in the same path as this file, which will -# import the compiled part of the wrapper from the _pyghex namespace. - -from .._pyghex import * # noqa:F403 diff --git a/bindings/python/src/ghex/util.py b/bindings/python/src/ghex/util.py index 1d92b1e79..3436bf999 100644 --- a/bindings/python/src/ghex/util.py +++ b/bindings/python/src/ghex/util.py @@ -12,7 +12,7 @@ import inspect from typing import TYPE_CHECKING -import ghex.pyghex as _pyghex +from ghex import pyghex if TYPE_CHECKING: from numpy.typing import DTypeLike @@ -30,7 +30,7 @@ def unwrap(arg: Any) -> Any: def cls_from_cpp_type_spec(cpp_type_spec: Union[str, tuple[str, ...]]) -> Any: if isinstance(cpp_type_spec, str): - return getattr(_pyghex, cpp_type_spec) + return getattr(pyghex, cpp_type_spec) else: fq_cpp_type_name, *template_args = cpp_type_spec template_args = [ @@ -39,7 +39,7 @@ def cls_from_cpp_type_spec(cpp_type_spec: Union[str, tuple[str, ...]]) -> Any: ] fq_cpp_type_specialization_name = fq_cpp_type_name + "_" + "_".join(template_args) + "_" - return getattr(_pyghex, fq_cpp_type_specialization_name) + return getattr(pyghex, fq_cpp_type_specialization_name) class CppWrapper: diff --git a/bindings/python/src/_pyghex/CMakeLists.txt b/bindings/python/src/pyghex/CMakeLists.txt similarity index 73% rename from bindings/python/src/_pyghex/CMakeLists.txt rename to bindings/python/src/pyghex/CMakeLists.txt index 7f52ee9fa..a01c82a06 100644 --- a/bindings/python/src/_pyghex/CMakeLists.txt +++ b/bindings/python/src/pyghex/CMakeLists.txt @@ -1,3 +1,5 @@ +set(CMAKE_CXX_STANDARD 17) + set(pyghex_source config.cpp context_shim.cpp @@ -25,7 +27,6 @@ set_property(TARGET pyghex_obj PROPERTY POSITION_INDEPENDENT_CODE ON) target_include_directories(pyghex_obj PRIVATE $ ) -target_link_libraries(pyghex_obj PRIVATE pybind11::module) ghex_target_compile_options(pyghex_obj) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") target_compile_options(pyghex_obj PRIVATE -fbracket-depth=512) @@ -40,27 +41,38 @@ compile_as_cuda(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} SOURCES ${pyghex_source}) # ================== # Create the Python module in the build directory. # The module contains the dynamic library, __init__.py and VERSION information. -add_library(pyghex MODULE $) -# With this, the full name of the library will be something like: -# _pyghex.cpython-36m-x86_64-linux-gnu.so +nanobind_add_module(pyghex $) + +target_link_libraries(pyghex_obj PRIVATE nanobind-static) +# Choose this particular output directory for testing purposes set_target_properties(pyghex PROPERTIES - OUTPUT_NAME _pyghex - PREFIX "${PYTHON_MODULE_PREFIX}" - SUFFIX "${PYTHON_MODULE_EXTENSION}" - # Choose this particular output directory for testing purposes - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../../ghex") + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../../ghex/") # This dependency has to be spelt out again, despite being added to # pyghex_obj because CMake. -target_link_libraries(pyghex PRIVATE pybind11::module) target_link_libraries(pyghex PRIVATE ghex) ghex_link_to_oomph(ghex) +# Generate the type stub if version permits it. +if ((DEFINED nanobind_VERSION) AND ("${nanobind_VERSION}" VERSION_GREATER "2.3")) + nanobind_add_stub( + pyghex_stub + MODULE pyghex + OUTPUT pyghex.pyi + RECURSIVE # We need to specify `RECURSIVE` to enable `OUTPUT_PATH`. + PYTHON_PATH $ + OUTPUT_PATH $ + DEPENDS pyghex + ) +endif() + # Set RPaths such that the python module is able to find libghex include(ghex_rpath) if (SKBUILD_PROJECT_NAME) + # TODO: Include the stub file here such that it is installed. set_target_properties(pyghex PROPERTIES INSTALL_RPATH "${rpath_origin_install_libdir}") install(TARGETS pyghex DESTINATION .) else() + # TODO: Include the stub file here such that it is installed. set_target_properties(pyghex PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") install(TARGETS pyghex DESTINATION ${GHEX_PYTHON_LIB_PATH}/ghex) endif() diff --git a/bindings/python/src/_pyghex/config.cpp b/bindings/python/src/pyghex/config.cpp similarity index 52% rename from bindings/python/src/_pyghex/config.cpp rename to bindings/python/src/pyghex/config.cpp index 2e725c724..49ec4f8fe 100644 --- a/bindings/python/src/_pyghex/config.cpp +++ b/bindings/python/src/pyghex/config.cpp @@ -11,8 +11,8 @@ #include #include -#include -#include +#include +#include #include @@ -22,38 +22,37 @@ namespace pyghex // Returns a python dictionary that python users can use to look up // which options the GHEX library was configured with at compile time. -pybind11::dict +nanobind::dict config() { #define mk_str(x) mk_tok(x) #define mk_tok(x) #x - pybind11::dict dict; + nanobind::dict dict; - dict[pybind11::str("transport")] = pybind11::str(mk_str(GHEX_TRANSPORT_BACKEND)); + dict[nanobind::str("transport")] = nanobind::str(mk_str(GHEX_TRANSPORT_BACKEND)); #ifdef GHEX_USE_GPU - dict[pybind11::str("gpu")] = pybind11::bool_(true); + dict[nanobind::str("gpu")] = nanobind::bool_(true); #else - dict[pybind11::str("gpu")] = pybind11::bool_(false); + dict[nanobind::str("gpu")] = nanobind::bool_(false); #endif - dict[pybind11::str("gpu_mode")] = pybind11::str(mk_str(GHEX_GPU_MODE)); + dict[nanobind::str("gpu_mode")] = nanobind::str(mk_str(GHEX_GPU_MODE)); #ifdef GHEX_USE_XPMEM - dict[pybind11::str("xpmem")] = pybind11::bool_(true); + dict[nanobind::str("xpmem")] = nanobind::bool_(true); #else - dict[pybind11::str("xpmem")] = pybind11::bool_(false); + dict[nanobind::str("xpmem")] = nanobind::bool_(false); #endif #define mk_ver(M, m, p) mk_tok(M) "." mk_tok(m) "." mk_tok(p) { const char* version = mk_ver(GHEX_VERSION_MAJOR, GHEX_VERSION_MINOR, GHEX_VERSION_PATCH); - dict[pybind11::str("version")] = pybind11::str(version); + dict[nanobind::str("version")] = nanobind::str(version); } { - const char* version = - mk_ver(PYBIND11_VERSION_MAJOR, PYBIND11_VERSION_MINOR, PYBIND11_VERSION_PATCH); - dict[pybind11::str("pybind-version")] = pybind11::str(version); + const char* version = mk_ver(NB_VERSION_MAJOR, NB_VERSION_MINOR, NB_VERSION_PATCH); + dict[nanobind::str("nanobind-version")] = nanobind::str(version); } #undef mk_str #undef mk_ver @@ -63,27 +62,27 @@ config() } void -print_config(const pybind11::dict& d) +print_config(const nanobind::dict& d) { std::stringstream s; s << "GHEX's configuration:\n"; for (auto x : d) { - s << " " << std::left << std::setw(16) << x.first << ": " << std::right << std::setw(10) - << x.second << "\n"; + s << " " << std::left << std::setw(16) << nanobind::cast(x.first) << ": " + << std::right << std::setw(10) << nanobind::str(x.second).c_str() << "\n"; } - pybind11::print(s.str()); + nanobind::print(s.str().c_str()); } // Register configuration void -register_config(pybind11::module& m) +register_config(nanobind::module_& m) { m.def("config", &config, "Get GHEX's configuration.") .def( - "print_config", [](const pybind11::dict& d) { return print_config(d); }, + "print_config", [](const nanobind::dict& d) { return print_config(d); }, "Print GHEX's configuration."); } } // namespace pyghex diff --git a/bindings/python/src/_pyghex/context_shim.cpp b/bindings/python/src/pyghex/context_shim.cpp similarity index 84% rename from bindings/python/src/_pyghex/context_shim.cpp rename to bindings/python/src/pyghex/context_shim.cpp index 7c8db752d..031221a54 100644 --- a/bindings/python/src/_pyghex/context_shim.cpp +++ b/bindings/python/src/pyghex/context_shim.cpp @@ -8,7 +8,8 @@ * SPDX-License-Identifier: BSD-3-Clause */ #include -#include +#include +#include #include #include @@ -35,13 +36,13 @@ to_string(const context_shim& c) } // namespace util void -register_context(pybind11::module& m) +register_context(nanobind::module_& m) { using namespace std::string_literals; - using namespace pybind11::literals; + using namespace nanobind::literals; - pybind11::class_(m, "context") - .def(pybind11::init(), "mpi_comm"_a, "thread_safe"_a = false, + nanobind::class_(m, "context") + .def(nanobind::init(), "mpi_comm"_a, "thread_safe"_a = false, "Create a ghex context") .def("__str__", util::to_string) .def("__repr__", util::to_string) diff --git a/bindings/python/src/_pyghex/context_shim.hpp b/bindings/python/src/pyghex/context_shim.hpp similarity index 100% rename from bindings/python/src/_pyghex/context_shim.hpp rename to bindings/python/src/pyghex/context_shim.hpp diff --git a/bindings/python/src/_pyghex/module.cpp b/bindings/python/src/pyghex/module.cpp similarity index 56% rename from bindings/python/src/_pyghex/module.cpp rename to bindings/python/src/pyghex/module.cpp index 5729291ee..4ed46dde4 100644 --- a/bindings/python/src/_pyghex/module.cpp +++ b/bindings/python/src/pyghex/module.cpp @@ -7,43 +7,41 @@ * Please, refer to the LICENSE file in the root directory. * SPDX-License-Identifier: BSD-3-Clause */ -#include - -namespace py = pybind11; +#include namespace pyghex { -void register_config(pybind11::module& m); -void register_mpi(pybind11::module& m); -void register_context(pybind11::module& m); -void register_py_dtype_to_cpp_name(pybind11::module& m); +void register_config(nanobind::module_& m); +void register_mpi(nanobind::module_& m); +void register_context(nanobind::module_& m); +void register_py_dtype_to_cpp_name(nanobind::module_& m); namespace structured { namespace regular { -void register_domain_descriptor(pybind11::module& m); -void register_halo_generator(pybind11::module& m); -void register_field_descriptor(pybind11::module& m); -void register_pattern(pybind11::module& m); -void register_communication_object(pybind11::module& m); +void register_domain_descriptor(nanobind::module_& m); +void register_halo_generator(nanobind::module_& m); +void register_field_descriptor(nanobind::module_& m); +void register_pattern(nanobind::module_& m); +void register_communication_object(nanobind::module_& m); } // namespace regular } // namespace structured namespace unstructured { -void register_domain_descriptor(pybind11::module& m); -void register_halo_generator(pybind11::module& m); -void register_field_descriptor(pybind11::module& m); -void register_pattern(pybind11::module& m); -void register_communication_object(pybind11::module& m); +void register_domain_descriptor(nanobind::module_& m); +void register_halo_generator(nanobind::module_& m); +void register_field_descriptor(nanobind::module_& m); +void register_pattern(nanobind::module_& m); +void register_communication_object(nanobind::module_& m); } // namespace unstructured } // namespace pyghex -PYBIND11_MODULE(_pyghex, m) +NB_MODULE(pyghex, m) { - m.doc() = "pybind11 ghex bindings"; // optional module docstring + m.doc() = "nanobind ghex bindings"; pyghex::register_config(m); pyghex::register_mpi(m); diff --git a/bindings/python/src/_pyghex/mpi_comm_shim.cpp b/bindings/python/src/pyghex/mpi_comm_shim.cpp similarity index 77% rename from bindings/python/src/_pyghex/mpi_comm_shim.cpp rename to bindings/python/src/pyghex/mpi_comm_shim.cpp index 73d94c773..6dcd3b2c9 100644 --- a/bindings/python/src/_pyghex/mpi_comm_shim.cpp +++ b/bindings/python/src/pyghex/mpi_comm_shim.cpp @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include @@ -30,11 +32,11 @@ namespace // Convert a Python object to an MPI Communicator. inline MPI_Comm -convert_to_mpi_comm(pybind11::object o) +convert_to_mpi_comm(nanobind::object o) { - if (!pybind11::hasattr(o, "py2f")) - throw pybind11::type_error("Argument must be `mpi4py.MPI.Comm`"); - return MPI_Comm_f2c(o.attr("py2f")().cast()); + if (!nanobind::hasattr(o, "py2f")) + throw nanobind::type_error("Argument must be `mpi4py.MPI.Comm`"); + return MPI_Comm_f2c(nanobind::cast(o.attr("py2f")())); } } // anonymous namespace @@ -55,7 +57,7 @@ to_string(const mpi_comm_shim& c) } } // namespace util -mpi_comm_shim::mpi_comm_shim(pybind11::object o) { comm = convert_to_mpi_comm(o); } +mpi_comm_shim::mpi_comm_shim(nanobind::object o) { comm = convert_to_mpi_comm(o); } void mpi_init(bool threadsafe) @@ -93,15 +95,14 @@ mpi_is_finalized() } void -register_mpi(pybind11::module& m) +register_mpi(nanobind::module_& m) { using namespace std::string_literals; - using namespace pybind11::literals; + using namespace nanobind::literals; - pybind11::class_ mpi_comm(m, "mpi_comm"); - mpi_comm.def(pybind11::init<>()) - .def(pybind11::init([](pybind11::object o) { return mpi_comm_shim(o); }), "mpi_comm_obj"_a, - "MPI communicator object.") + nanobind::class_ mpi_comm(m, "mpi_comm"); + mpi_comm.def(nanobind::init<>()) + .def(nanobind::init(), "mpi_comm_obj"_a, "MPI communicator object.") .def("__str__", util::to_string) .def("__repr__", util::to_string); diff --git a/bindings/python/src/_pyghex/mpi_comm_shim.hpp b/bindings/python/src/pyghex/mpi_comm_shim.hpp similarity index 86% rename from bindings/python/src/_pyghex/mpi_comm_shim.hpp rename to bindings/python/src/pyghex/mpi_comm_shim.hpp index 67541d617..ccaeb6da7 100644 --- a/bindings/python/src/_pyghex/mpi_comm_shim.hpp +++ b/bindings/python/src/pyghex/mpi_comm_shim.hpp @@ -10,7 +10,7 @@ #pragma once #include -#include +#include #include namespace pyghex @@ -19,7 +19,7 @@ namespace pyghex // This is a slight variation of Arbor's mpi_comm_shim as introduced here // https://github.com/arbor-sim/arbor/commit/1d6a48d0ce4b96f59acf931efd61d55c571c5e68 // A shim is required for MPI_Comm, because OpenMPI defines it as a pointer to -// a forward-declared type, which pybind11 won't allow as an argument. +// a forward-declared type, which nanobind won't allow as an argument. // MPICH and its derivatives use an integer. struct mpi_comm_shim { @@ -30,7 +30,7 @@ struct mpi_comm_shim : comm(c) { } - mpi_comm_shim(pybind11::object o); + mpi_comm_shim(nanobind::object o); }; namespace util diff --git a/bindings/python/src/pyghex/py_dtype_to_cpp_name.cpp b/bindings/python/src/pyghex/py_dtype_to_cpp_name.cpp new file mode 100644 index 000000000..96838a5af --- /dev/null +++ b/bindings/python/src/pyghex/py_dtype_to_cpp_name.cpp @@ -0,0 +1,82 @@ +/* + * ghex-org + * + * Copyright (c) 2014-2025, ETH Zurich + * All rights reserved. + * + * Please, refer to the LICENSE file in the root directory. + * SPDX-License-Identifier: BSD-3-Clause + */ +#include + +#include +#include +#include + +#include +#include + +#include +#include + +namespace pyghex +{ + +namespace +{ +nanobind::object +numpy_dtype(const nanobind::handle& value) +{ + return nanobind::module_::import_("numpy").attr("dtype")( + nanobind::borrow(value)); +} + +template +nanobind::object +dtype_of() +{ + if constexpr (std::is_same_v) return numpy_dtype(nanobind::str("float64")); + else if constexpr (std::is_same_v) + return numpy_dtype(nanobind::str("float32")); + else if constexpr (std::is_same_v) + return numpy_dtype(nanobind::str("bool")); + else if constexpr (std::is_same_v) + return numpy_dtype(nanobind::str("int32")); + else if constexpr (std::is_same_v) + return numpy_dtype(nanobind::str("int64")); + else + static_assert(sizeof(T) == 0, "unsupported dtype"); +} +} // namespace + +std::string +py_dtype_to_cpp_name(nanobind::handle dtype) +{ + const auto canonical_dtype = numpy_dtype(dtype); + std::string cpp_name; + + gridtools::for_each( + [&cpp_name, &canonical_dtype](auto l) + { + using type = decltype(l); + + auto candidate_dtype = dtype_of(); + if (nanobind::bool_(canonical_dtype.equal(candidate_dtype))) + { + assert(cpp_name.empty()); + cpp_name = util::mangle_python(); + } + }); + + if (cpp_name.empty()) { throw std::invalid_argument("Unsupported numpy dtype"); } + + return cpp_name; +} + +void +register_py_dtype_to_cpp_name(nanobind::module_& m) +{ + m.def("py_dtype_to_cpp_name", &py_dtype_to_cpp_name, "Convert numpy dtype to C++ type name"); +} + +} // namespace pyghex diff --git a/bindings/python/src/_pyghex/register_class.hpp b/bindings/python/src/pyghex/register_class.hpp similarity index 68% rename from bindings/python/src/_pyghex/register_class.hpp rename to bindings/python/src/pyghex/register_class.hpp index d33f669ec..2f43ac747 100644 --- a/bindings/python/src/_pyghex/register_class.hpp +++ b/bindings/python/src/pyghex/register_class.hpp @@ -11,21 +11,21 @@ #include -#include -#include +#include +#include namespace pyghex { template auto -register_class(pybind11::module& m) +register_class(nanobind::module_& m) { auto demangled = util::demangle(); auto pymangled = util::mangle_python(demangled); - return pybind11::class_(m, pymangled.c_str()) - .def_property_readonly_static("__cpp_type__", - [demangled](const pybind11::object&) { return demangled; }) + return nanobind::class_(m, pymangled.c_str()) + .def_prop_ro_static("__cpp_type__", + [demangled](const nanobind::object&) { return demangled; }) .def("__str__", [pymangled](const T&) { return ""; }) .def("__repr__", [pymangled](const T&) { return ""; }); } diff --git a/bindings/python/src/_pyghex/structured/regular/communication_object.cpp b/bindings/python/src/pyghex/structured/regular/communication_object.cpp similarity index 86% rename from bindings/python/src/_pyghex/structured/regular/communication_object.cpp rename to bindings/python/src/pyghex/structured/regular/communication_object.cpp index d46d2de19..bb24c832e 100644 --- a/bindings/python/src/_pyghex/structured/regular/communication_object.cpp +++ b/bindings/python/src/pyghex/structured/regular/communication_object.cpp @@ -17,6 +17,10 @@ #include #include +#include +#include +#include + namespace pyghex { namespace structured @@ -25,7 +29,7 @@ namespace regular { void -register_communication_object(pybind11::module& m) +register_communication_object(nanobind::module_& m) { auto _communication_object = register_class(m); @@ -34,7 +38,7 @@ register_communication_object(pybind11::module& m) [&m, &_communication_object](auto l) { using namespace std::string_literals; - using namespace pybind11::literals; + using namespace nanobind::literals; using communication_object_type = gridtools::meta::first; using handle_type = typename communication_object_type::handle_type; using pattern_type = typename communication_object_type::pattern_type; @@ -59,28 +63,28 @@ register_communication_object(pybind11::module& m) "exchange", [](communication_object_shim& co, std::vector b) { return co.exchange(b.begin(), b.end()); }, - pybind11::keep_alive<0, 1>()) + nanobind::keep_alive<0, 1>()) .def( "exchange", [](communication_object_shim& co, buffer_info_type& b) - { return co.exchange(b); }, pybind11::keep_alive<0, 1>()) + { return co.exchange(b); }, nanobind::keep_alive<0, 1>()) .def( "exchange", [](communication_object_shim& co, buffer_info_type& b0, buffer_info_type& b1) { return co.exchange(b0, b1); }, - pybind11::keep_alive<0, 1>()) + nanobind::keep_alive<0, 1>()) .def( "exchange", [](communication_object_shim& co, buffer_info_type& b0, buffer_info_type& b1, buffer_info_type& b2) { return co.exchange(b0, b1, b2); }, - pybind11::keep_alive<0, 1>()); + nanobind::keep_alive<0, 1>()); }); }); m.def( "make_co_regular", [](context_shim& c) { return communication_object_shim{&c.m, std::monostate{}}; }, - pybind11::keep_alive<0, 1>()); + nanobind::keep_alive<0, 1>()); } } //namespace regular diff --git a/bindings/python/src/_pyghex/structured/regular/communication_object.hpp b/bindings/python/src/pyghex/structured/regular/communication_object.hpp similarity index 100% rename from bindings/python/src/_pyghex/structured/regular/communication_object.hpp rename to bindings/python/src/pyghex/structured/regular/communication_object.hpp diff --git a/bindings/python/src/_pyghex/structured/regular/domain_descriptor.cpp b/bindings/python/src/pyghex/structured/regular/domain_descriptor.cpp similarity index 86% rename from bindings/python/src/_pyghex/structured/regular/domain_descriptor.cpp rename to bindings/python/src/pyghex/structured/regular/domain_descriptor.cpp index fafdf6c50..f635cbe6a 100644 --- a/bindings/python/src/_pyghex/structured/regular/domain_descriptor.cpp +++ b/bindings/python/src/pyghex/structured/regular/domain_descriptor.cpp @@ -16,6 +16,11 @@ #include #include +#include +#include +#include +#include + namespace pyghex { namespace structured @@ -40,14 +45,14 @@ as_tuple(const std::array& arr) } // namespace void -register_domain_descriptor(pybind11::module& m) +register_domain_descriptor(nanobind::module_& m) { gridtools::for_each< gridtools::meta::transform>( [&m](auto l) { using namespace std::string_literals; - using namespace pybind11::literals; + using namespace nanobind::literals; using type = gridtools::meta::first; using domain_id_type = typename type::domain_id_type; @@ -57,7 +62,7 @@ register_domain_descriptor(pybind11::module& m) auto _domain_descriptor = register_class(m); _domain_descriptor - .def(pybind11::init(), "domain_id"_a, "first"_a, + .def(nanobind::init(), "domain_id"_a, "first"_a, "last"_a, "Create a domain descriptor") .def("domain_id", &type::domain_id, "Returns the domain id") .def( diff --git a/bindings/python/src/_pyghex/structured/regular/domain_descriptor.hpp b/bindings/python/src/pyghex/structured/regular/domain_descriptor.hpp similarity index 100% rename from bindings/python/src/_pyghex/structured/regular/domain_descriptor.hpp rename to bindings/python/src/pyghex/structured/regular/domain_descriptor.hpp diff --git a/bindings/python/src/pyghex/structured/regular/field_descriptor.cpp b/bindings/python/src/pyghex/structured/regular/field_descriptor.cpp new file mode 100644 index 000000000..953eb872e --- /dev/null +++ b/bindings/python/src/pyghex/structured/regular/field_descriptor.cpp @@ -0,0 +1,150 @@ +/* + * ghex-org + * + * Copyright (c) 2014-2023, ETH Zurich + * All rights reserved. + * + * Please, refer to the LICENSE file in the root directory. + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace pyghex +{ +namespace structured +{ +namespace regular +{ +namespace +{ +template +using int_tuple_constant = gridtools::meta::list...>; + +template +struct ndarray_device_type; + +template<> +struct ndarray_device_type +{ + using type = nanobind::device::cpu; +}; + +template<> +struct ndarray_device_type +{ +#ifdef __HIP_PLATFORM_HCC__ + using type = nanobind::device::rocm; +#else + using type = nanobind::device::cuda; +#endif +}; + +template +std::vector +byte_strides(const T& b, std::ptrdiff_t itemsize) +{ + std::vector result(b.ndim()); + for (size_t i = 0; i < b.ndim(); ++i) result[i] = b.stride(i) * itemsize; + return result; +} + +} // namespace + +void +register_field_descriptor(nanobind::module_& m) +{ + gridtools::for_each< + gridtools::meta::transform>( + [&m](auto l) + { + using namespace std::string_literals; + using namespace nanobind::literals; + + using field_descriptor_type = gridtools::meta::first; + using T = typename field_descriptor_type::value_type; + using domain_id_type = typename field_descriptor_type::domain_id_type; + using domain_descriptor_type = typename field_descriptor_type::domain_descriptor_type; + using arch_type = typename field_descriptor_type::arch_type; + using layout_map = typename field_descriptor_type::layout_map; + using dimension = typename field_descriptor_type::dimension; + using array = std::array; + using grid_type = ghex::structured::grid::template type; + using pattern_type = ghex::pattern; + using buffer_info_type = + ghex::buffer_info; + + auto _field_descriptor = register_class(m); + register_class(m); + + auto make_field_descriptor = + [](const domain_descriptor_type& dom, + nanobind::ndarray::type> b, + const array& offsets, const array& extents) + { + if (b.ndim() != dimension::value) + { + std::stringstream error; + error << "Field has wrong dimensions. Expected " << dimension::value + << ", but got " << b.ndim(); + throw nanobind::type_error(error.str().c_str()); + } + + auto strides = byte_strides(b, sizeof(T)); + + auto ordered_strides = strides; + std::sort(ordered_strides.begin(), ordered_strides.end(), + [](std::ptrdiff_t a, std::ptrdiff_t b) { return a > b; }); + + array b_layout_map; + for (size_t i = 0; i < dimension::value; ++i) + { + auto it = std::find(ordered_strides.begin(), ordered_strides.end(), strides[i]); + b_layout_map[i] = std::distance(ordered_strides.begin(), it); + if (b_layout_map[i] != layout_map::at(i)) + { + throw nanobind::type_error("Buffer has a different layout than specified."); + } + } + + return ghex::wrap_field(dom, static_cast(b.data()), + offsets, extents, strides); + }; + +#if NB_VERSION_MAJOR < 2 + _field_descriptor.def( + "__init__", + [make_field_descriptor](field_descriptor_type* t, const domain_descriptor_type& dom, + nanobind::ndarray::type> b, + const array& offsets, const array& extents) + { new (t) field_descriptor_type(make_field_descriptor(dom, b, offsets, extents)); }, + nanobind::keep_alive<1, 3>()); +#else + _field_descriptor.def(nanobind::new_(make_field_descriptor), + nanobind::keep_alive<0, 3>()); +#endif + }); +} + +} // namespace regular +} // namespace structured +} // namespace pyghex diff --git a/bindings/python/src/_pyghex/structured/regular/field_descriptor.hpp b/bindings/python/src/pyghex/structured/regular/field_descriptor.hpp similarity index 100% rename from bindings/python/src/_pyghex/structured/regular/field_descriptor.hpp rename to bindings/python/src/pyghex/structured/regular/field_descriptor.hpp diff --git a/bindings/python/src/_pyghex/structured/regular/halo_generator.cpp b/bindings/python/src/pyghex/structured/regular/halo_generator.cpp similarity index 66% rename from bindings/python/src/_pyghex/structured/regular/halo_generator.cpp rename to bindings/python/src/pyghex/structured/regular/halo_generator.cpp index a54e68515..d99d62c85 100644 --- a/bindings/python/src/_pyghex/structured/regular/halo_generator.cpp +++ b/bindings/python/src/pyghex/structured/regular/halo_generator.cpp @@ -13,6 +13,11 @@ #include #include +#include +#include +#include +#include + namespace pyghex { namespace structured @@ -21,14 +26,14 @@ namespace regular { void -register_halo_generator(pybind11::module& m) +register_halo_generator(nanobind::module_& m) { gridtools::for_each< gridtools::meta::transform>( [&m](auto l) { using namespace std::string_literals; - using namespace pybind11::literals; + using namespace nanobind::literals; using type = gridtools::meta::first; using dimension = typename type::dimension; @@ -43,23 +48,27 @@ register_halo_generator(pybind11::module& m) auto _box2 = register_class(m); _halo_generator - .def(pybind11::init(), "first"_a, + .def(nanobind::init(), "first"_a, "last"_a, "halos"_a, "periodic"_a, "Create a halo generator") - .def("__call__", &type::operator()); + .def("__call__", + [](const type& halo_gen, const typename type::domain_type& domain) + { + nanobind::list result; + for (const auto& halo : halo_gen(domain)) + result.append(nanobind::cast(halo)); + return result; + }); - _box2 - .def_property_readonly("local", - pybind11::overload_cast<>(&box2::local, pybind11::const_)) - .def_property_readonly("global_", - pybind11::overload_cast<>(&box2::global, pybind11::const_)); + _box2.def_prop_ro("local", [](const box2& b) { return b.local(); }) + .def_prop_ro("global_", [](const box2& b) { return b.global(); }); - _box.def_property_readonly("first", + _box.def_prop_ro("first", [](const box& b) { auto first = b.first(); return static_cast(first); }) - .def_property_readonly("last", + .def_prop_ro("last", [](const box& b) { auto last = b.last(); diff --git a/bindings/python/src/_pyghex/structured/regular/halo_generator.hpp b/bindings/python/src/pyghex/structured/regular/halo_generator.hpp similarity index 100% rename from bindings/python/src/_pyghex/structured/regular/halo_generator.hpp rename to bindings/python/src/pyghex/structured/regular/halo_generator.hpp diff --git a/bindings/python/src/_pyghex/structured/regular/pattern.cpp b/bindings/python/src/pyghex/structured/regular/pattern.cpp similarity index 85% rename from bindings/python/src/_pyghex/structured/regular/pattern.cpp rename to bindings/python/src/pyghex/structured/regular/pattern.cpp index 629b870b3..cd2cdcff2 100644 --- a/bindings/python/src/_pyghex/structured/regular/pattern.cpp +++ b/bindings/python/src/pyghex/structured/regular/pattern.cpp @@ -17,6 +17,10 @@ #include #include +#include +#include +#include + namespace pyghex { namespace structured @@ -25,14 +29,14 @@ namespace regular { void -register_pattern(pybind11::module& m) +register_pattern(nanobind::module_& m) { gridtools::for_each< gridtools::meta::transform>( [&m](auto l) { using namespace std::string_literals; - using namespace pybind11::literals; + using namespace nanobind::literals; using type = gridtools::meta::first; using halo_gen = typename type::halo_gen; @@ -48,14 +52,14 @@ register_pattern(pybind11::module& m) auto _pattern_container = register_class(m); _pattern_container - .def_property_readonly_static("grid_type", [](const pybind11::object&) + .def_prop_ro_static("grid_type", [](const nanobind::object&) { return util::mangle_python(); }) - .def_property_readonly_static("domain_id_type", [](const pybind11::object&) + .def_prop_ro_static("domain_id_type", [](const nanobind::object&) { return util::mangle_python(); }); m.def( "make_pattern_regular", [](context_shim& c, halo_gen& h, domain_range& d) - { return ghex::make_pattern(c.m, h, d); }, pybind11::keep_alive<0, 1>()); + { return ghex::make_pattern(c.m, h, d); }, nanobind::keep_alive<0, 1>()); gridtools::for_each>( [&m, &_pattern_container](auto k) @@ -66,7 +70,7 @@ register_pattern(pybind11::module& m) // "identifier undefined in device code" error when using NVCC _pattern_container.def( "__call__", [](const pattern_container& pattern, field& f) - { return pattern(f); }, pybind11::keep_alive<0, 2>()); + { return pattern(f); }, nanobind::keep_alive<0, 2>()); }); }); } diff --git a/bindings/python/src/_pyghex/structured/regular/pattern.hpp b/bindings/python/src/pyghex/structured/regular/pattern.hpp similarity index 100% rename from bindings/python/src/_pyghex/structured/regular/pattern.hpp rename to bindings/python/src/pyghex/structured/regular/pattern.hpp diff --git a/bindings/python/src/_pyghex/structured/types.hpp b/bindings/python/src/pyghex/structured/types.hpp similarity index 100% rename from bindings/python/src/_pyghex/structured/types.hpp rename to bindings/python/src/pyghex/structured/types.hpp diff --git a/bindings/python/src/_pyghex/types.hpp b/bindings/python/src/pyghex/types.hpp similarity index 100% rename from bindings/python/src/_pyghex/types.hpp rename to bindings/python/src/pyghex/types.hpp diff --git a/bindings/python/src/_pyghex/unstructured/communication_object.cpp b/bindings/python/src/pyghex/unstructured/communication_object.cpp similarity index 72% rename from bindings/python/src/_pyghex/unstructured/communication_object.cpp rename to bindings/python/src/pyghex/unstructured/communication_object.cpp index 66de91433..ffb74687c 100644 --- a/bindings/python/src/_pyghex/unstructured/communication_object.cpp +++ b/bindings/python/src/pyghex/unstructured/communication_object.cpp @@ -24,6 +24,10 @@ #include #include +#include +#include +#include + namespace pyghex { namespace unstructured @@ -32,7 +36,7 @@ namespace { #if defined(GHEX_CUDACC) cudaStream_t -extract_cuda_stream(pybind11::object python_stream) +extract_cuda_stream(nanobind::object python_stream) { static_assert(std::is_pointer::value); if (python_stream.is_none()) @@ -42,56 +46,56 @@ extract_cuda_stream(pybind11::object python_stream) } else { - if (pybind11::hasattr(python_stream, "__cuda_stream__")) + if (nanobind::hasattr(python_stream, "__cuda_stream__")) { // CUDA stream protocol: https://nvidia.github.io/cuda-python/cuda-core/latest/interoperability.html#cuda-stream-protocol - pybind11::tuple cuda_stream_protocol = - pybind11::getattr(python_stream, "__cuda_stream__")(); + nanobind::tuple cuda_stream_protocol = + nanobind::cast(python_stream.attr("__cuda_stream__")()); if (cuda_stream_protocol.size() != 2) { std::stringstream error; error << "Expected a tuple of length 2, but got one with length " << cuda_stream_protocol.size(); - throw pybind11::type_error(error.str()); + throw nanobind::type_error(error.str().c_str()); } - //Currently there is only version 0. - const auto protocol_version = cuda_stream_protocol[0].cast(); + const auto protocol_version = nanobind::cast(cuda_stream_protocol[0]); if (protocol_version != 0) { std::stringstream error; error << "Expected `__cuda_stream__` protocol version 0, but got " << protocol_version; - throw pybind11::type_error(error.str()); + throw nanobind::type_error(error.str().c_str()); } - const auto stream_address = cuda_stream_protocol[1].cast(); + const auto stream_address = nanobind::cast(cuda_stream_protocol[1]); return reinterpret_cast(stream_address); } - else if (pybind11::hasattr(python_stream, "ptr")) + else if (nanobind::hasattr(python_stream, "ptr")) { // CuPy stream: See https://docs.cupy.dev/en/latest/reference/generated/cupy.cuda.Stream.html#cupy-cuda-stream - std::uintptr_t stream_address = python_stream.attr("ptr").cast(); + std::uintptr_t stream_address = + nanobind::cast(python_stream.attr("ptr")); return reinterpret_cast(stream_address); } // TODO: Find out of how to extract the typename, i.e. `type(python_stream).__name__`. std::stringstream error; error << "Failed to convert the stream object into a CUDA stream."; - throw pybind11::type_error(error.str()); + throw nanobind::type_error(error.str().c_str()); } } #endif } // namespace void -register_communication_object(pybind11::module& m) +register_communication_object(nanobind::module_& m) { gridtools::for_each< gridtools::meta::transform>( [&m](auto l) { using namespace std::string_literals; - using namespace pybind11::literals; + using namespace nanobind::literals; using type = gridtools::meta::first; using handle = typename type::handle_type; @@ -106,9 +110,9 @@ register_communication_object(pybind11::module& m) #if defined(GHEX_CUDACC) .def( "schedule_wait", - [](typename type::handle_type& h, pybind11::object python_stream) + [](typename type::handle_type& h, nanobind::object python_stream) { return h.schedule_wait(extract_cuda_stream(python_stream)); }, - pybind11::keep_alive<0, 1>()) + nanobind::keep_alive<0, 1>()) #endif .def("is_ready", &handle::is_ready) .def("progress", &handle::progress); @@ -124,52 +128,52 @@ register_communication_object(pybind11::module& m) .def( "exchange", [](type& co, std::vector b) { return co.exchange(b.begin(), b.end()); }, - pybind11::keep_alive<0, 1>()) + nanobind::keep_alive<0, 1>()) .def( "exchange", [](type& co, buffer_info_type& b) - { return co.exchange(b); }, pybind11::keep_alive<0, 1>()) + { return co.exchange(b); }, nanobind::keep_alive<0, 1>()) .def( "exchange", [](type& co, buffer_info_type& b0, buffer_info_type& b1) - { return co.exchange(b0, b1); }, pybind11::keep_alive<0, 1>()) + { return co.exchange(b0, b1); }, nanobind::keep_alive<0, 1>()) .def( "exchange", [](type& co, buffer_info_type& b0, buffer_info_type& b1, buffer_info_type& b2) { return co.exchange(b0, b1, b2); }, - pybind11::keep_alive<0, 1>()) + nanobind::keep_alive<0, 1>()) #if defined(GHEX_CUDACC) .def( "schedule_exchange", - [](type& co, pybind11::object python_stream, + [](type& co, nanobind::object python_stream, std::vector b) { return co.schedule_exchange(extract_cuda_stream(python_stream), b.begin(), b.end()); }, - pybind11::keep_alive<0, 1>(), pybind11::arg("stream"), - pybind11::arg("patterns")) + nanobind::keep_alive<0, 1>(), nanobind::arg("stream"), + nanobind::arg("patterns")) .def( "schedule_exchange", - [](type& co, pybind11::object python_stream, buffer_info_type& b) + [](type& co, nanobind::object python_stream, buffer_info_type& b) { return co.schedule_exchange(extract_cuda_stream(python_stream), b); }, - pybind11::keep_alive<0, 1>(), pybind11::arg("stream"), - pybind11::arg("b")) + nanobind::keep_alive<0, 1>(), nanobind::arg("stream"), + nanobind::arg("b")) .def( "schedule_exchange", - [](type& co, pybind11::object python_stream, buffer_info_type& b0, + [](type& co, nanobind::object python_stream, buffer_info_type& b0, buffer_info_type& b1) { return co.schedule_exchange(extract_cuda_stream(python_stream), b0, b1); }, - pybind11::keep_alive<0, 1>(), pybind11::arg("stream"), - pybind11::arg("b0"), pybind11::arg("b1")) + nanobind::keep_alive<0, 1>(), nanobind::arg("stream"), + nanobind::arg("b0"), nanobind::arg("b1")) .def( "schedule_exchange", - [](type& co, pybind11::object python_stream, buffer_info_type& b0, + [](type& co, nanobind::object python_stream, buffer_info_type& b0, buffer_info_type& b1, buffer_info_type& b2) { return co.schedule_exchange(extract_cuda_stream(python_stream), b0, b1, b2); }, - pybind11::keep_alive<0, 1>(), pybind11::arg("stream"), - pybind11::arg("b0"), pybind11::arg("b1"), pybind11::arg("b2")) + nanobind::keep_alive<0, 1>(), nanobind::arg("stream"), + nanobind::arg("b0"), nanobind::arg("b1"), nanobind::arg("b2")) .def("has_scheduled_exchange", [](type& co) -> bool { return co.has_scheduled_exchange(); }) #endif // end scheduled exchange @@ -178,7 +182,7 @@ register_communication_object(pybind11::module& m) m.def( "make_co_unstructured", [](context_shim& c) { return type{c.m}; }, - pybind11::keep_alive<0, 1>()); + nanobind::keep_alive<0, 1>()); m.def("expose_cpp_ptr", [](type* obj) { return reinterpret_cast(obj); }); diff --git a/bindings/python/src/_pyghex/unstructured/communication_object.hpp b/bindings/python/src/pyghex/unstructured/communication_object.hpp similarity index 100% rename from bindings/python/src/_pyghex/unstructured/communication_object.hpp rename to bindings/python/src/pyghex/unstructured/communication_object.hpp diff --git a/bindings/python/src/_pyghex/unstructured/domain_descriptor.cpp b/bindings/python/src/pyghex/unstructured/domain_descriptor.cpp similarity index 74% rename from bindings/python/src/_pyghex/unstructured/domain_descriptor.cpp rename to bindings/python/src/pyghex/unstructured/domain_descriptor.cpp index 2f6af561b..d353dda40 100644 --- a/bindings/python/src/_pyghex/unstructured/domain_descriptor.cpp +++ b/bindings/python/src/pyghex/unstructured/domain_descriptor.cpp @@ -15,19 +15,23 @@ #include #include +#include +#include +#include + namespace pyghex { namespace unstructured { void -register_domain_descriptor(pybind11::module& m) +register_domain_descriptor(nanobind::module_& m) { gridtools::for_each< gridtools::meta::transform>( [&m](auto l) { using namespace std::string_literals; - using namespace pybind11::literals; + using namespace nanobind::literals; using type = gridtools::meta::first; using domain_id_type = typename type::domain_id_type; @@ -37,12 +41,21 @@ register_domain_descriptor(pybind11::module& m) auto _domain_descriptor = register_class(m); _domain_descriptor - .def(pybind11::init( +#if NB_VERSION_MAJOR < 2 + .def("__init__", + [](type* t, domain_id_type id, const std::vector& gids, + const std::vector& halo_lids) { + new (t) + type{id, gids.begin(), gids.end(), halo_lids.begin(), halo_lids.end()}; + }) +#else + .def(nanobind::new_( [](domain_id_type id, const std::vector& gids, const std::vector& halo_lids) { return type{id, gids.begin(), gids.end(), halo_lids.begin(), halo_lids.end()}; })) +#endif .def("domain_id", &type::domain_id, "Returns the domain id") .def("size", &type::size, "Returns the size") .def("inner_size", &type::inner_size, "Returns the inner size") diff --git a/bindings/python/src/_pyghex/unstructured/domain_descriptor.hpp b/bindings/python/src/pyghex/unstructured/domain_descriptor.hpp similarity index 100% rename from bindings/python/src/_pyghex/unstructured/domain_descriptor.hpp rename to bindings/python/src/pyghex/unstructured/domain_descriptor.hpp diff --git a/bindings/python/src/pyghex/unstructured/field_descriptor.cpp b/bindings/python/src/pyghex/unstructured/field_descriptor.cpp new file mode 100644 index 000000000..790853dbd --- /dev/null +++ b/bindings/python/src/pyghex/unstructured/field_descriptor.cpp @@ -0,0 +1,166 @@ +/* + * ghex-org + * + * Copyright (c) 2014-2023, ETH Zurich + * All rights reserved. + * + * Please, refer to the LICENSE file in the root directory. + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace pyghex +{ +namespace unstructured +{ + +namespace +{ +template +struct ndarray_device_type; + +template<> +struct ndarray_device_type +{ + using type = nanobind::device::cpu; +}; + +template<> +struct ndarray_device_type +{ +#ifdef __HIP_PLATFORM_HCC__ + using type = nanobind::device::rocm; +#else + using type = nanobind::device::cuda; +#endif +}; + +} // namespace + +void +register_field_descriptor(nanobind::module_& m) +{ + gridtools::for_each< + gridtools::meta::transform>( + [&m](auto l) + { + using namespace std::string_literals; + using namespace nanobind::literals; + + using type = gridtools::meta::first; + using T = typename type::value_type; + using domain_id_type = typename type::domain_id_type; + using domain_descriptor_type = typename type::domain_descriptor_type; + using arch_type = typename type::arch_type; + using grid_type = ghex::unstructured::grid::template type; + using pattern_type = ghex::pattern; + using buffer_info_type = ghex::buffer_info; + + auto _field_descriptor = register_class(m); + register_class(m); + + auto make_field_descriptor = + [](const domain_descriptor_type& dom, + nanobind::ndarray::type> b) + { + if (b.ndim() > 2u) + { + std::stringstream error; + error << "Field has too many dimensions. Expected at most 2, but got " + << b.ndim(); + throw nanobind::type_error(error.str().c_str()); + } + + if (static_cast(b.shape(0)) != dom.size()) + { + std::stringstream error; + error << "Field's first dimension (" << static_cast(b.shape(0)) + << ") must match the size of the domain (" << dom.size() << ")"; + throw nanobind::type_error(error.str().c_str()); + } + + bool levels_first = true; + std::size_t outer_strides = 0u; + const auto stride_0 = b.stride(0) * sizeof(T); + const auto stride_1 = + (b.ndim() == 2) ? (b.stride(1) * sizeof(T)) : std::ptrdiff_t{0}; + if (b.ndim() == 2 && stride_1 != sizeof(T)) + { + levels_first = false; + if (stride_0 != sizeof(T)) + { + std::stringstream error; + error << "Field's strides are not compatible with GHEX. Expected that the " + "(byte) stride of dimension 0 is " + << sizeof(T) << " but got " << (std::size_t)(stride_0) << "."; + throw nanobind::type_error(error.str().c_str()); + } + if (((std::size_t)(stride_1) % sizeof(T)) != 0) + { + std::stringstream error; + error << "Field's strides are not compatible with GHEX. Expected that the " + "(byte) stride of dimension 1 " + << (std::size_t)(stride_1) << " is a multiple of the element size " + << sizeof(T) << "."; + throw nanobind::type_error(error.str().c_str()); + } + outer_strides = stride_1 / sizeof(T); + } + else if (b.ndim() == 2) + { + // stride_1 == sizeof(T): levels are the inner (fast) dimension + if (((std::size_t)(stride_0) % sizeof(T)) != 0) + { + std::stringstream error; + error << "Field's strides are not compatible with GHEX. Expected that the " + "(byte) stride of dimension 0 " + << (std::size_t)(stride_0) << " is a multiple of the element size of " + << sizeof(T) << "."; + throw nanobind::type_error(error.str().c_str()); + } + outer_strides = stride_0 / sizeof(T); + } + else if (stride_0 != sizeof(T)) + { + std::stringstream error; + error + << "Field's strides are not compatible with GHEX. With one dimension expected " + "the stride to be " + << sizeof(T) << " but got " << stride_0 << "."; + throw nanobind::type_error(error.str().c_str()); + } + + const std::size_t levels = (b.ndim() == 1) ? 1u : (std::size_t)b.shape(1); + return type{dom, static_cast(b.data()), levels, levels_first, outer_strides}; + }; + +#if NB_VERSION_MAJOR < 2 + _field_descriptor.def( + "__init__", + [make_field_descriptor](type* t, const domain_descriptor_type& dom, + nanobind::ndarray::type> b) + { new (t) type(make_field_descriptor(dom, b)); }, + nanobind::keep_alive<1, 3>()); +#else + _field_descriptor.def(nanobind::new_(make_field_descriptor), + nanobind::keep_alive<0, 3>()); +#endif + }); +} + +} // namespace unstructured +} // namespace pyghex diff --git a/bindings/python/src/_pyghex/unstructured/field_descriptor.hpp b/bindings/python/src/pyghex/unstructured/field_descriptor.hpp similarity index 100% rename from bindings/python/src/_pyghex/unstructured/field_descriptor.hpp rename to bindings/python/src/pyghex/unstructured/field_descriptor.hpp diff --git a/bindings/python/src/_pyghex/unstructured/halo_generator.cpp b/bindings/python/src/pyghex/unstructured/halo_generator.cpp similarity index 75% rename from bindings/python/src/_pyghex/unstructured/halo_generator.cpp rename to bindings/python/src/pyghex/unstructured/halo_generator.cpp index f505c9586..dc5432898 100644 --- a/bindings/python/src/_pyghex/unstructured/halo_generator.cpp +++ b/bindings/python/src/pyghex/unstructured/halo_generator.cpp @@ -12,19 +12,23 @@ #include #include +#include +#include +#include + namespace pyghex { namespace unstructured { void -register_halo_generator(pybind11::module& m) +register_halo_generator(nanobind::module_& m) { gridtools::for_each< gridtools::meta::transform>( [&m](auto l) { using namespace std::string_literals; - using namespace pybind11::literals; + using namespace nanobind::literals; using type = gridtools::meta::first; using global_index_type = typename type::global_index_type; @@ -33,9 +37,8 @@ register_halo_generator(pybind11::module& m) auto _halo_generator = register_class(m); /*auto _halo = */ register_class(m); - _halo_generator.def(pybind11::init<>(), "Create a halo generator") - .def(pybind11::init( - [](const std::vector& gids) { return type{gids}; })) + _halo_generator.def(nanobind::init<>(), "Create a halo generator") + .def(nanobind::init>()) .def("__call__", &type::operator()); }); } diff --git a/bindings/python/src/_pyghex/unstructured/halo_generator.hpp b/bindings/python/src/pyghex/unstructured/halo_generator.hpp similarity index 100% rename from bindings/python/src/_pyghex/unstructured/halo_generator.hpp rename to bindings/python/src/pyghex/unstructured/halo_generator.hpp diff --git a/bindings/python/src/_pyghex/unstructured/pattern.cpp b/bindings/python/src/pyghex/unstructured/pattern.cpp similarity index 85% rename from bindings/python/src/_pyghex/unstructured/pattern.cpp rename to bindings/python/src/pyghex/unstructured/pattern.cpp index 01f1ae12a..15a0acc75 100644 --- a/bindings/python/src/_pyghex/unstructured/pattern.cpp +++ b/bindings/python/src/pyghex/unstructured/pattern.cpp @@ -19,20 +19,24 @@ #include #include +#include +#include +#include + namespace pyghex { namespace unstructured { void -register_pattern(pybind11::module& m) +register_pattern(nanobind::module_& m) { gridtools::for_each< gridtools::meta::transform>( [&m](auto l) { using namespace std::string_literals; - using namespace pybind11::literals; + using namespace nanobind::literals; using type = gridtools::meta::first; using halo_gen = typename type::halo_gen; @@ -46,14 +50,14 @@ register_pattern(pybind11::module& m) auto _pattern_container = register_class(m); _pattern_container - .def_property_readonly_static("grid_type", [](const pybind11::object&) + .def_prop_ro_static("grid_type", [](const nanobind::object&) { return util::mangle_python(); }) - .def_property_readonly_static("domain_id_type", [](const pybind11::object&) + .def_prop_ro_static("domain_id_type", [](const nanobind::object&) { return util::mangle_python(); }); m.def( "make_pattern_unstructured", [](context_shim& c, halo_gen& h, domain_range& d) - { return ghex::make_pattern(c.m, h, d); }, pybind11::keep_alive<0, 1>()); + { return ghex::make_pattern(c.m, h, d); }, nanobind::keep_alive<0, 1>()); gridtools::for_each>( [&m, &_pattern_container](auto k) @@ -64,7 +68,7 @@ register_pattern(pybind11::module& m) // "identifier undefined in device code" error when using NVCC _pattern_container.def( "__call__", [](const pattern_container& pattern, field& f) - { return pattern(f); }, pybind11::keep_alive<0, 2>()); + { return pattern(f); }, nanobind::keep_alive<0, 2>()); }); m.def("expose_cpp_ptr", diff --git a/bindings/python/src/_pyghex/unstructured/pattern.hpp b/bindings/python/src/pyghex/unstructured/pattern.hpp similarity index 100% rename from bindings/python/src/_pyghex/unstructured/pattern.hpp rename to bindings/python/src/pyghex/unstructured/pattern.hpp diff --git a/bindings/python/src/_pyghex/unstructured/types.hpp b/bindings/python/src/pyghex/unstructured/types.hpp similarity index 100% rename from bindings/python/src/_pyghex/unstructured/types.hpp rename to bindings/python/src/pyghex/unstructured/types.hpp diff --git a/bindings/python/src/_pyghex/util/demangle.hpp b/bindings/python/src/pyghex/util/demangle.hpp similarity index 100% rename from bindings/python/src/_pyghex/util/demangle.hpp rename to bindings/python/src/pyghex/util/demangle.hpp diff --git a/bindings/python/src/_pyghex/util/to_string.hpp b/bindings/python/src/pyghex/util/to_string.hpp similarity index 100% rename from bindings/python/src/_pyghex/util/to_string.hpp rename to bindings/python/src/pyghex/util/to_string.hpp diff --git a/cmake/ghex_find_python_module.cmake b/cmake/ghex_find_python_module.cmake index 2dc2ff835..818a7cf8e 100644 --- a/cmake/ghex_find_python_module.cmake +++ b/cmake/ghex_find_python_module.cmake @@ -5,7 +5,7 @@ function(find_python_module module) if(NOT PY_${module_upper}) if(ARGC GREATER 1 AND ARGV1 STREQUAL "REQUIRED") - set(${module}_FIND_REQUIRED TRUE) + set(PY_${module}_FIND_REQUIRED TRUE) endif() # A module's location is usually a directory, but for binary modules @@ -31,7 +31,7 @@ function(find_python_module module) else() set(HAVE_${module_upper} OFF CACHE INTERNAL "Python module available") endif() - endif(NOT PY_${module_upper}) + endif() find_package_handle_standard_args(PY_${module} DEFAULT_MSG PY_${module_upper}) endfunction(find_python_module) diff --git a/cmake/ghex_python.cmake b/cmake/ghex_python.cmake index d34246364..530d7d4a9 100644 --- a/cmake/ghex_python.cmake +++ b/cmake/ghex_python.cmake @@ -1,18 +1,32 @@ include(GNUInstallDirs) -set(PYBIND11_CPP_STANDARD -std=c++17) - if (GHEX_BUILD_PYTHON_BINDINGS) + include(ghex_find_python_module) - find_package (Python3 REQUIRED COMPONENTS Interpreter Development.Module) - - if(${Python3_FOUND}) - set(PYTHON_EXECUTABLE "${Python3_EXECUTABLE}") + find_package(Python 3 REQUIRED COMPONENTS Interpreter Development.Module) + set(PYTHON_EXECUTABLE "${Python_EXECUTABLE}") + + # Look for the `nanobind` Python module. + find_python_module(nanobind) + + if (SKBUILD_PROJECT_NAME) + message(STATUS "Building in pip mode.") + # Build as a Python package, `nanobind` is a buiild dependency, so it should be found. + if(NOT HAVE_NANOBIND) + message(FATAL_ERROR "Expected that the `nanobind` Python pakage was installed as dependency") + endif() + find_package(nanobind CONFIG REQUIRED HINTS "${PY_NANOBIND}/cmake") + elseif (HAVE_NANOBIND) + message(STATUS "Building in normal mode but use installed nanobind package.") + # Normal build and the `nanobind` Python package was found, use it. + find_package(nanobind CONFIG REQUIRED HINTS "${PY_NANOBIND}/cmake") + else() + message(STATUS "Building in normal mode but use system nanobind.") + # Normal build but no `nanobind` Python package was found, try to localize the one on the system. + # NOTE: The `CONFIG` is retained for compatibility with the old version, but maybe remove it. + find_package(nanobind CONFIG REQUIRED) endif() - include(ghex_find_python_module) - find_package(pybind11 REQUIRED PATHS ${Python_SITELIB}) - # Ask Python where it keeps its system (platform) packages. file(WRITE "${CMAKE_BINARY_DIR}/install-prefix" "${CMAKE_INSTALL_PREFIX}") execute_process( diff --git a/include/ghex/structured/regular/halo_generator.hpp b/include/ghex/structured/regular/halo_generator.hpp index c8f0fc5f3..9bdfbdd86 100644 --- a/include/ghex/structured/regular/halo_generator.hpp +++ b/include/ghex/structured/regular/halo_generator.hpp @@ -9,6 +9,8 @@ */ #pragma once +#include + #include #include #include diff --git a/pyproject.toml b/pyproject.toml index 0294fca52..4f6ea348e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,9 +2,8 @@ build-backend = 'scikit_build_core.build' requires = [ 'cmake', - 'pybind11>=2.6', - 'scikit-build-core', - 'wheel', + 'nanobind>=2.0.0', + 'scikit-build-core' ] [project] diff --git a/test/bindings/python/CMakeLists.txt b/test/bindings/python/CMakeLists.txt index 2069165a3..c48ea5c97 100644 --- a/test/bindings/python/CMakeLists.txt +++ b/test/bindings/python/CMakeLists.txt @@ -16,7 +16,7 @@ get_filename_component(pyghex_test_workdir ${python_mod_path}/.. ABSOLUTE) # command to create a virtual environment add_custom_command( OUTPUT ${venv_dir} - COMMAND ${Python3_EXECUTABLE} -m venv ${venv_dir} + COMMAND ${Python_EXECUTABLE} -m venv ${venv_dir} COMMENT "Creating virtual environment for test dependencies" ) @@ -48,9 +48,10 @@ add_custom_target( # setup test target # ================= -add_custom_target(pyghex_tests) -add_dependencies(pyghex pyghex_tests) -add_dependencies(setup_test_env pyghex_tests) +add_custom_target(pyghex_tests ALL) +add_dependencies(pyghex_tests pyghex) +add_dependencies(pyghex_tests pyghex_files) +add_dependencies(pyghex_tests setup_test_env) copy_files(TARGET pyghex_tests DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/fixtures FILES ${CMAKE_CURRENT_SOURCE_DIR}/fixtures/context.py)