Skip to content

Migrate to nanobind, remove pybind11 - #595

Open
BenWibking wants to merge 12 commits into
AMReX-Codes:developmentfrom
BenWibking:nanobind
Open

Migrate to nanobind, remove pybind11#595
BenWibking wants to merge 12 commits into
AMReX-Codes:developmentfrom
BenWibking:nanobind

Conversation

@BenWibking

@BenWibking BenWibking commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

This is a vibe-coded experiment to see if it is feasible to replace pybind11 with nanobind. Apparently, it is!

Benefits:

  • "~4× faster compile time"
  • "~5× smaller binaries"
  • " ~10× lower runtime overheads compared to pybind11"
  • Hidden bonus feature: DLPack batteries included via nb::ndarray<...>

Generated using ~1 million tokens from GPT-5.6-Sol.

@BenWibking BenWibking mentioned this pull request Jul 19, 2026
@ax3l

ax3l commented Jul 19, 2026

Copy link
Copy Markdown
Member

Cool! @BenWibking van you try to use nanobind's stub generator to replace our pybind11-stubgen? We need stubs for docs and ipython completion.

Comment thread CMakeLists.txt
${pyAMReX_SOURCE_DIR}/src/amrex/space${D}d/amrex_${D}d_pybind/__init__.pyi
PYTHON_PATH $<TARGET_FILE_DIR:pyAMReX_${D}d>
DEPENDS pyAMReX_${D}d
)

@BenWibking BenWibking Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@ax3l nanobind stubs are generated here using nanobind's CMake interface: https://nanobind.readthedocs.io/en/latest/typing.html#cmake-interface

@BenWibking

Copy link
Copy Markdown
Contributor Author

Amazingly, all tests pass on all compilers. I'm marking it ready for review.

@BenWibking
BenWibking marked this pull request as ready for review July 19, 2026 03:38
@BenWibking BenWibking changed the title [Experiment] Nanobind 2026 Migrate to nanobind, remove pybind11 Jul 19, 2026
@ax3l ax3l self-assigned this Jul 19, 2026
@ax3l
ax3l self-requested a review July 19, 2026 04:30
Comment thread src/AmrCore/TagBox.cpp


void init_TagBox (py::module& m)
void init_TagBox (nb::module_& m)
Comment thread src/Base/IntVect.cpp
{
template<int dim>
void init_IntVectND(py::module &m)
void init_IntVectND(nb::module_ &m)
Comment thread src/Base/RealBox.cpp


void init_RealBox(py::module &m) {
void init_RealBox(nb::module_ &m) {
Comment thread src/Base/RealVect.cpp


void init_RealVect(py::module &m) {
void init_RealVect(nb::module_ &m) {


void init_PhysBCFunct(py::module& m)
void init_PhysBCFunct(nb::module_& m)
Comment thread src/Base/FabArray.cpp
{
template<typename T>
void make_FabArray_T(py::module &m, std::string const &name)
void make_FabArray_T(nb::module_ &m, std::string const &name)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't understand this comment. @ax3l do you?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Huh, I think that is a false positive.

ArrayOfStructs<T_ParticleType, amrex::PinnedArenaAllocator> h_data;
h_data.resize(aos.size());
//py::array_t<T_ParticleType> h_data(aos.size());
//nb::array_t<T_ParticleType> h_data(aos.size());

@ax3l ax3l left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This looks pretty great!

I added a few inline thoughts to research.

I will push a commit to undo the .pyi changes and might isolate the setup.py binary distr update once I understand it to a separate PR.

cmake_minimum_required(VERSION 3.24)
project(pyAMReXDownstreamNanobind LANGUAGES C CXX)

find_package(Python 3.8 COMPONENTS Interpreter Development.Module REQUIRED)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh, that's a neat downstream integration test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The AI came up with that after it broke on an earlier change. I think it's useful.

Comment thread CMakeLists.txt
PYTHON_PATH $<TARGET_FILE_DIR:pyAMReX_${D}d>
DEPENDS pyAMReX_${D}d
)
add_dependencies(pyAMReX_stubs pyAMReX_${D}d_stub)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We might need to add a dependency pyAMReX_${D}d_stub -> pyAMReX_${D}d as well.

Maybe also env var helpers in case we do not want to rely on a pip_install (to check).

Comment thread setup.py Outdated
Comment thread setup.py Outdated
@BenWibking

Copy link
Copy Markdown
Contributor Author

I was confused to whether the .pyi files should be checked in. What is the policy on that? Is it documented somewhere?

@ax3l

ax3l commented Jul 29, 2026

Copy link
Copy Markdown
Member

Sorry for the confusion.

Yes, the .pyi files are checked in, because we need them to build Sphinx docs from.
(We have not enough CPU hours to rebuild the C++ bindings in RTD CI builders.)
Additionally, tools like IPython's Jedi auto-complete need them to give interactive users auto-completion hints.

But, they are only checked in via an auto-commit on development.
The reason for that is that if PRs change them, then nearly every PR will have a merge conflict with other open PRs. (I tried for a few days and realized that does not scale.)

Thus, our PR's CIs do build them, check they are valid, but they do not commit them.
Only the development branch does the last step, too: commit on changes.

yes, we should document this workflow / automation in the development or maintenance sections of the docs:
https://pyamrex.readthedocs.io

@BenWibking

Copy link
Copy Markdown
Contributor Author

Sorry for the confusion.

Yes, the .pyi files are checked in, because we need them to build Sphinx docs from.

But, they are only checked in via an auto-commit on development. The reason for that is that if PRs change them, then nearly every PR will have a merge conflict with other open PRs. So that does not scale.

ah, okay :) That makes sense.

@ax3l

ax3l commented Jul 29, 2026

Copy link
Copy Markdown
Member

Before we merge this in (maybe early in a release cycle), I would do a whole BLAST build test to see if mixing nanobind and pybind11 works well for the mid-term, then migrate the rest of BLAST too. (I expect no issues.)

@BenWibking

Copy link
Copy Markdown
Contributor Author

Ok, I can extract the build fixes to a separate PR. I have some uncommitted fixes to make things easier when using uv to build/install.

BenWibking and others added 11 commits July 29, 2026 14:53
We do not want to modify the generated stub files on this branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolutions port the new features to nanobind:
- dependencies.json: keep nanobind pins, take new AMReX commit
- PlotFileUtil.cpp: port write_multi_level_plotfile to nb::
- ParticleHeader.cpp: port new bindings to nb:: (def_rw, nb::arg)
- ParticleContainer.H, pyAMReX.cpp: nb:: for merged declarations

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@BenWibking

Copy link
Copy Markdown
Contributor Author

Build fixes moved to #599.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants