Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ jobs:
- name: Install packages
run: |
apt update
apt-get -y install python3-dev python3-venv python3-pybind11 ninja-build
apt-get -y install python3-dev python3-venv nanobind-dev ninja-build

- name: Clone w/ submodules
uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test_pip.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
9 changes: 4 additions & 5 deletions bindings/python/src/_pyghex/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
set(CMAKE_CXX_STANDARD 17)

set(pyghex_source
config.cpp
context_shim.cpp
Expand Down Expand Up @@ -25,7 +27,6 @@ set_property(TARGET pyghex_obj PROPERTY POSITION_INDEPENDENT_CODE ON)
target_include_directories(pyghex_obj PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
)
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)
Expand All @@ -40,18 +41,16 @@ 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 $<TARGET_OBJECTS:pyghex_obj>)
nanobind_add_module(pyghex $<TARGET_OBJECTS:pyghex_obj>)
target_link_libraries(pyghex_obj PRIVATE nanobind-static)
# With this, the full name of the library will be something like:
# _pyghex.cpython-36m-x86_64-linux-gnu.so
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")
# 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)

Expand Down
39 changes: 19 additions & 20 deletions bindings/python/src/_pyghex/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
#include <ios>
#include <sstream>

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <nanobind/nanobind.h>
#include <nanobind/stl/string.h>

#include <ghex/config.hpp>

Expand All @@ -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
Expand All @@ -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<std::string>(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
11 changes: 6 additions & 5 deletions bindings/python/src/_pyghex/context_shim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <cstdint>
#include <pybind11/pybind11.h>
#include <nanobind/nanobind.h>
#include <nanobind/stl/string.h>

#include <context_shim.hpp>
#include <util/to_string.hpp>
Expand All @@ -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_<context_shim>(m, "context")
.def(pybind11::init<mpi_comm_shim, bool>(), "mpi_comm"_a, "thread_safe"_a = false,
nanobind::class_<context_shim>(m, "context")
.def(nanobind::init<mpi_comm_shim, bool>(), "mpi_comm"_a, "thread_safe"_a = false,
"Create a ghex context")
.def("__str__", util::to_string<context_shim>)
.def("__repr__", util::to_string<context_shim>)
Expand Down
36 changes: 17 additions & 19 deletions bindings/python/src/_pyghex/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,41 @@
* Please, refer to the LICENSE file in the root directory.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <pybind11/pybind11.h>

namespace py = pybind11;
#include <nanobind/nanobind.h>

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);
Expand Down
23 changes: 12 additions & 11 deletions bindings/python/src/_pyghex/mpi_comm_shim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include <string>
#include <sstream>

#include <nanobind/nanobind.h>
#include <nanobind/stl/string.h>
#include <mpi_comm_shim.hpp>
#include <util/to_string.hpp>

Expand All @@ -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<MPI_Fint>());
if (!nanobind::hasattr(o, "py2f"))
throw nanobind::type_error("Argument must be `mpi4py.MPI.Comm`");
return MPI_Comm_f2c(nanobind::cast<MPI_Fint>(o.attr("py2f")()));
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

convert_to_mpi_comm does not actually call py2f(). o.attr("py2f") returns the bound method object, so casting it to MPI_Fint will fail at runtime. Call the method and cast its result (i.e., invoke py2f before converting to MPI_Comm).

Suggested change
return MPI_Comm_f2c(nanobind::cast<MPI_Fint>(o.attr("py2f")()));
auto py2f_method = o.attr("py2f");
auto f_comm_obj = py2f_method();
MPI_Fint f_comm = nanobind::cast<MPI_Fint>(f_comm_obj);
return MPI_Comm_f2c(f_comm);

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot double check this. o.attr("py2f")() does call the method, right? Perhaps counting parentheses is not your strength (or it's not mine)?

}

} // anonymous namespace
Expand All @@ -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)
Expand Down Expand Up @@ -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_shim> 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_shim> mpi_comm(m, "mpi_comm");
mpi_comm.def(nanobind::init<>())
.def(nanobind::init<nanobind::object>(), "mpi_comm_obj"_a, "MPI communicator object.")
.def("__str__", util::to_string<mpi_comm_shim>)
.def("__repr__", util::to_string<mpi_comm_shim>);

Expand Down
6 changes: 3 additions & 3 deletions bindings/python/src/_pyghex/mpi_comm_shim.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#pragma once

#include <mpi.h>
#include <pybind11/pybind11.h>
#include <nanobind/nanobind.h>
#include <util/to_string.hpp>

namespace pyghex
Expand All @@ -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
{
Expand All @@ -30,7 +30,7 @@ struct mpi_comm_shim
: comm(c)
{
}
mpi_comm_shim(pybind11::object o);
mpi_comm_shim(nanobind::object o);
};

namespace util
Expand Down
Loading
Loading