Skip to content

Commit

Permalink
Merge pull request #1256 from glotzerlab/pybind11-box
Browse files Browse the repository at this point in the history
Convert Box Module to Use nanobind
  • Loading branch information
tommy-waltmann authored Jun 20, 2024
2 parents 935a3c8 + 33326d1 commit 7d0ebd5
Show file tree
Hide file tree
Showing 13 changed files with 681 additions and 505 deletions.
33 changes: 26 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# CMake 3.12.0 is the oldest version supported by the way freud links TBB to
# object libraries like _cluster. This is also the oldest version tested in CI.
cmake_minimum_required(VERSION 3.12.0)
cmake_minimum_required(VERSION 3.15...3.30)

project(freud)

Expand All @@ -27,6 +25,19 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
# https://stackoverflow.com/questions/50600708/combining-cmake-object-libraries-with-shared-libraries
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
add_subdirectory(CMake)

# find python
if(CMAKE_VERSION VERSION_LESS 3.18)
set(DEV_MODULE Development)
else()
set(DEV_MODULE Development.Module)
endif()

find_package(
Python 3.8
COMPONENTS Interpreter ${DEV_MODULE}
REQUIRED)

find_package_config_first(TBB)

if(TBB_FOUND)
Expand All @@ -36,6 +47,18 @@ if(TBB_FOUND)
"[${TBB_LIBRARY}][${TBB_INCLUDE_DIR}]")
endif()

# go find nanobind
execute_process(
COMMAND ${Python_EXECUTABLE} "-m" "nanobind" "--cmake_dir"
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE nanobind_ROOT)
find_package(nanobind CONFIG REQUIRED)
if(nanobind_FOUND)
find_package_message(
nanobind "Found nanobind: ${nanobind_DIR} ${nanobind_VERSION}"
"[${nanobind_DIR},${nanobind_VERSION}]")
endif()

# Fail fast if users have not cloned submodules.
if(NOT WIN32)
string(ASCII 27 Esc)
Expand Down Expand Up @@ -76,7 +99,3 @@ set(ignoreMe "${SKBUILD}")

add_subdirectory(cpp)
add_subdirectory(freud)

if(_using_conda OR DEFINED ENV{CIBUILDWHEEL})
set_target_properties(libfreud PROPERTIES INSTALL_RPATH_USE_LINK_PATH True)
endif()
34 changes: 25 additions & 9 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,31 @@ if(WIN32)
add_compile_options(/DNOMINMAX)
endif()

add_subdirectory(cluster)
add_subdirectory(density)
add_subdirectory(diffraction)
add_subdirectory(environment)
add_subdirectory(locality)
add_subdirectory(order)
add_subdirectory(parallel)
add_subdirectory(pmft)
add_subdirectory(util)
# Detect when building against a conda environment set the _using_conda variable
# for use both in this file and in the parent
get_filename_component(_python_bin_dir ${PYTHON_EXECUTABLE} DIRECTORY)
if(EXISTS "${_python_bin_dir}/../conda-meta")
message("-- Detected conda environment, setting INSTALL_RPATH_USE_LINK_PATH")
set(_using_conda On)
set(_using_conda
On
PARENT_SCOPE)
else()
set(_using_conda Off)
set(_using_conda
Off
PARENT_SCOPE)
endif()

add_subdirectory(box)

# commented out for now, uncomment them as the conversion to pybind11 progresses
# add_subdirectory(cluster) add_subdirectory(density)
# add_subdirectory(diffraction) add_subdirectory(environment)
# add_subdirectory(locality) add_subdirectory(order) add_subdirectory(parallel)
# add_subdirectory(pmft) add_subdirectory(util)

#[[
add_library(
libfreud SHARED
$<TARGET_OBJECTS:_cluster>
Expand Down Expand Up @@ -53,3 +68,4 @@ if(CMAKE_EXPORT_COMPILE_COMMANDS)
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/compile_commands.json
${PROJECT_SOURCE_DIR})
endif()
]]
38 changes: 11 additions & 27 deletions cpp/box/Box.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
#ifndef BOX_H
#define BOX_H

#include "utils.h"
#include <algorithm>
#include <complex>
#include <sstream>
#include <stdexcept>

#include "VectorMath.h"
#include "utils.h"

/*! \file Box.h
\brief Represents simulation boxes and contains helpful wrapping functions.
Expand Down Expand Up @@ -93,12 +93,6 @@ class Box
return !(*this == b);
}

//! Set L, box lengths, inverses. Box is also centered at zero.
void setL(const vec3<float>& L)
{
setL(L.x, L.y, L.z);
}

//! Set L, box lengths, inverses. Box is also centered at zero.
void setL(const float Lx, const float Ly, const float Lz)
{
Expand Down Expand Up @@ -156,9 +150,9 @@ class Box
}

//! Get current stored inverse of L
vec3<float> getLinv() const
std::vector<float> getLinv() const
{
return m_Linv;
return {m_Linv.x, m_Linv.y, m_Linv.z};
}

//! Get tilt factor xy
Expand Down Expand Up @@ -227,10 +221,9 @@ class Box
//! Convert fractional coordinates into absolute coordinates in place
/*! \param vecs Vectors of fractional coordinates between 0 and 1 within
* parallelepipedal box
* \param Nvecs Number of vectors
* \param out The array in which to place the wrapped vectors.
* \param out_data The array in which to place the wrapped vectors.
*/
void makeAbsolute(const vec3<float>* vecs, unsigned int Nvecs, vec3<float>* out) const
void makeAbsolute(const vec3<float>* vecs, const unsigned int Nvecs, vec3<float>* out) const
{
util::forLoopWrapper(0, Nvecs, [&](size_t begin, size_t end) {
for (size_t i = begin; i < end; ++i)
Expand Down Expand Up @@ -294,12 +287,12 @@ class Box
* \param Nvecs Number of vectors
\param res Array to save the images
*/
void getImages(vec3<float>* vecs, unsigned int Nvecs, vec3<int>* res) const
void getImages(const vec3<float>* vecs, unsigned int Nvecs, vec3<int>* images) const
{
util::forLoopWrapper(0, Nvecs, [&](size_t begin, size_t end) {
for (size_t i = begin; i < end; ++i)
{
getImage(vecs[i], res[i]);
getImage(vecs[i], images[i]);
}
});
}
Expand Down Expand Up @@ -374,7 +367,7 @@ class Box
* \param masses Optional array of masses, of length Nvecs
* \return Center of mass as a vec3<float>
*/
vec3<float> centerOfMass(vec3<float>* vecs, size_t Nvecs, const float* masses = nullptr) const
vec3<float> centerOfMass(vec3<float>* vecs, size_t Nvecs, const float* masses) const
{
// This roughly follows the implementation in
// https://en.wikipedia.org/wiki/Center_of_mass#Systems_with_periodic_boundary_conditions
Expand All @@ -386,7 +379,7 @@ class Box
vec3<float> phase(constants::TWO_PI * makeFractional(vecs[i]));
vec3<std::complex<float>> xi(std::polar(float(1.0), phase.x), std::polar(float(1.0), phase.y),
std::polar(float(1.0), phase.z));
float mass = (masses != nullptr) ? masses[i] : float(1.0);
float mass = masses[i];
total_mass += mass;
xi_mean += std::complex<float>(mass, 0) * xi;
}
Expand All @@ -401,7 +394,7 @@ class Box
* \param Nvecs Number of vectors
* \param masses Optional array of masses, of length Nvecs
*/
void center(vec3<float>* vecs, unsigned int Nvecs, const float* masses = nullptr) const
void center(vec3<float>* vecs, unsigned int Nvecs, const float* masses) const
{
vec3<float> com(centerOfMass(vecs, Nvecs, masses));
util::forLoopWrapper(0, Nvecs, [&](size_t begin, size_t end) {
Expand Down Expand Up @@ -433,10 +426,6 @@ class Box
void computeDistances(const vec3<float>* query_points, const unsigned int n_query_points,
const vec3<float>* points, const unsigned int n_points, float* distances) const
{
if (n_query_points != n_points)
{
throw std::invalid_argument("The number of query points and points must match.");
}
util::forLoopWrapper(0, n_query_points, [&](size_t begin, size_t end) {
for (size_t i = begin; i < end; ++i)
{
Expand Down Expand Up @@ -473,7 +462,7 @@ class Box
\param n_points The number of points.
\param contains_mask Mask of points inside the box.
*/
void contains(const vec3<float>* points, const unsigned int n_points, bool* contains_mask) const
void contains(vec3<float>* points, const unsigned int n_points, bool* contains_mask) const
{
util::forLoopWrapper(0, n_points, [&](size_t begin, size_t end) {
std::transform(&points[begin], &points[end], &contains_mask[begin],
Expand Down Expand Up @@ -528,11 +517,6 @@ class Box
/*! \param periodic Flags to set
* \post Period flags are set to \a periodic
*/
void setPeriodic(vec3<bool> periodic)
{
m_periodic = periodic;
}

void setPeriodic(bool x, bool y, bool z)
{
m_periodic = vec3<bool>(x, y, z);
Expand Down
22 changes: 22 additions & 0 deletions cpp/box/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# create the target and add the needed properties
nanobind_add_module(_box SHARED module-box.cc export_Box.cc)
target_link_libraries(_box PUBLIC TBB::tbb)
# this probably isn't needed for box, but may be needed by other modules
# target_compile_definitions( box # Avoid deprecation warnings for unsupported
# NumPy API versions. See #
# https://numpy.org/doc/1.19/reference/c-api/deprecations.html PRIVATE
# "NPY_NO_DEPRECATED_API=NPY_1_10_API_VERSION" # Default voro++ verbosity is
# high. PRIVATE "VOROPP_VERBOSE=1")

if(APPLE)
set_target_properties(_box PROPERTIES INSTALL_RPATH "@loader_path")
else()
set_target_properties(_box PROPERTIES INSTALL_RPATH "\$ORIGIN")
endif()

if(_using_conda OR DEFINED ENV{CIBUILDWHEEL})
set_target_properties(_box PROPERTIES INSTALL_RPATH_USE_LINK_PATH True)
endif()

# install
install(TARGETS _box DESTINATION freud)
107 changes: 107 additions & 0 deletions cpp/box/export_Box.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) 2010-2024 The Regents of the University of Michigan
// This file is from the freud project, released under the BSD 3-Clause License.

#include "export_Box.h"

namespace nb = nanobind;

namespace freud { namespace box { namespace wrap {

void makeAbsolute(std::shared_ptr<Box> box, nb_array<float, nb::shape<-1, 3>> vecs,
nb_array<float, nb::shape<-1, 3>> out)
{
unsigned int Nvecs = vecs.shape(0);
vec3<float>* vecs_data = (vec3<float>*) (vecs.data());
vec3<float>* out_data = (vec3<float>*) (out.data());
box->makeAbsolute(vecs_data, Nvecs, out_data);
}

void makeFractional(std::shared_ptr<Box> box, nb_array<float, nb::shape<-1, 3>> vecs,
nb_array<float, nb::shape<-1, 3>> out)
{
unsigned int Nvecs = vecs.shape(0);
vec3<float>* vecs_data = (vec3<float>*) (vecs.data());
vec3<float>* out_data = (vec3<float>*) (out.data());
box->makeFractional(vecs_data, Nvecs, out_data);
}

void getImages(std::shared_ptr<Box> box, nb_array<float, nb::shape<-1, 3>> vecs,
nb_array<int, nb::shape<-1, 3>> images)
{
const unsigned int Nvecs = vecs.shape(0);
vec3<float>* vecs_data = (vec3<float>*) (vecs.data());
vec3<int>* images_data = (vec3<int>*) (images.data());
box->getImages(vecs_data, Nvecs, images_data);
}

void wrap(std::shared_ptr<Box> box, nb_array<float, nb::shape<-1, 3>> vecs,
nb_array<float, nb::shape<-1, 3>> out)
{
const unsigned int Nvecs = vecs.shape(0);
vec3<float>* vecs_data = (vec3<float>*) (vecs.data());
vec3<float>* out_data = (vec3<float>*) (out.data());
box->wrap(vecs_data, Nvecs, out_data);
}

void unwrap(std::shared_ptr<Box> box, nb_array<float> vecs, nb_array<int> images, nb_array<float> out)
{
const unsigned int Nvecs = vecs.shape(0);
vec3<float>* vecs_data = (vec3<float>*) (vecs.data());
vec3<int>* images_data = (vec3<int>*) (images.data());
vec3<float>* out_data = (vec3<float>*) (out.data());
box->unwrap(vecs_data, images_data, Nvecs, out_data);
}

std::vector<float> centerOfMass(std::shared_ptr<Box> box, nb_array<float> vecs,
nb_array<float, nb::shape<-1>> masses)
{
const unsigned int Nvecs = vecs.shape(0);
vec3<float>* vecs_data = (vec3<float>*) (vecs.data());
float* masses_data = (float*) (masses.data());
auto com = box->centerOfMass(vecs_data, Nvecs, masses_data);
return {com.x, com.y, com.z};
}

void center(std::shared_ptr<Box> box, nb_array<float> vecs, nb_array<float, nb::ndim<1>> masses)
{
const unsigned int Nvecs = vecs.shape(0);
vec3<float>* vecs_data = (vec3<float>*) (vecs.data());
float* masses_data = (float*) (masses.data());
box->center(vecs_data, Nvecs, masses_data);
}

void computeDistances(std::shared_ptr<Box> box, nb_array<float> query_points, nb_array<float> points,
nb_array<float, nb::ndim<1>> distances)
{
const unsigned int n_query_points = query_points.shape(0);
vec3<float>* query_points_data = (vec3<float>*) (query_points.data());
const unsigned int n_points = points.shape(0);
vec3<float>* points_data = (vec3<float>*) (points.data());
float* distances_data = (float*) (distances.data());
if (n_query_points != n_points)
{
throw std::invalid_argument("The number of query points and points must match.");
}
box->computeDistances(query_points_data, n_query_points, points_data, n_points, distances_data);
}

void computeAllDistances(std::shared_ptr<Box> box, nb_array<float> query_points, nb_array<float> points,
nb_array<float, nb::ndim<2>> distances)
{
const unsigned int n_query_points = query_points.shape(0);
vec3<float>* query_points_data = (vec3<float>*) (query_points.data());
const unsigned int n_points = points.shape(0);
vec3<float>* points_data = (vec3<float>*) (points.data());
float* distances_data = (float*) (distances.data());
box->computeAllDistances(query_points_data, n_query_points, points_data, n_points, distances_data);
}

void contains(std::shared_ptr<Box> box, nb_array<float> points, nb_array<bool, nb::ndim<1>> contains_mask)
{
const unsigned int n_points = points.shape(0);
vec3<float>* points_data = (vec3<float>*) (points.data());
bool* contains_mask_data = (bool*) (contains_mask.data());
box->contains(points_data, n_points, contains_mask_data);
}

}; }; }; // namespace freud::box::wrap
Loading

0 comments on commit 7d0ebd5

Please sign in to comment.