Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nanobind port: Cluster module #1282

Merged
merged 9 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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
26 changes: 15 additions & 11 deletions freud/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ add_library(
# box
box/Box.h
# cluster
# cluster/Cluster.h
# cluster/Cluster.cc
# cluster/ClusterProperties.h
# cluster/ClusterProperties.cc
cluster/Cluster.h
cluster/Cluster.cc
cluster/ClusterProperties.h
cluster/ClusterProperties.cc
# density
# density/CorrelationFunction.h
# density/CorrelationFunction.cc
Expand Down Expand Up @@ -133,9 +133,12 @@ set_target_properties(freud PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_link_libraries(freud PUBLIC TBB::tbb)
target_include_directories(freud SYSTEM PUBLIC ${PROJECT_SOURCE_DIR}/extern/)

# cluster nanobind_add_module(_cluster cluster/...)
# target_link_libraries(_cluster PUBLIC freud TBB::tbb)
# target_set_install_rpath(_cluster)
# cluster
nanobind_add_module(
_cluster cluster/module-Cluster.cc cluster/export-Cluster.cc
cluster/export-ClusterProperties.cc)
target_link_libraries(_cluster PUBLIC freud TBB::tbb)
target_set_install_rpath(_cluster)

# density nanobind_add_module(_density density/...)
# target_link_libraries(_density PUBLIC freud TBB::tbb)
Expand Down Expand Up @@ -193,6 +196,7 @@ target_set_install_rpath(_util)
set(python_files
__init__.py
box.py
cluster.py
data.py
errors.py
locality.py
Expand All @@ -202,17 +206,17 @@ set(python_files
interface.py
plot.py
util.py)
# cluster.py density.py diffraction.py environment.py order.py interface.py)
# density.py diffraction.py environment.py order.py)

copy_files_to_build("${python_files}" "freud" "*.py")

# install
if(SKBUILD)
install(FILES ${python_files} DESTINATION freud)
install(TARGETS _box DESTINATION freud)
# install(TARGETS _cluster DESTINATION freud) install(TARGETS _density
# DESTINATION freud) install(TARGETS _diffraction DESTINATION freud)
# install(TARGETS _environment DESTINATION freud)
install(TARGETS _cluster DESTINATION freud)
# install(TARGETS _density DESTINATION freud) install(TARGETS _diffraction
# DESTINATION freud) install(TARGETS _environment DESTINATION freud)
install(TARGETS _locality DESTINATION freud)
# install(TARGETS _order DESTINATION freud)
install(TARGETS _parallel DESTINATION freud)
Expand Down
7 changes: 3 additions & 4 deletions freud/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# 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.


# cluster,; density,; diffraction,; environment,; interface,; msd,; order,
from . import box, data, interface, locality, msd, parallel, pmft
# density,; diffraction,; environment,; order,
from . import box, cluster, data, interface, locality, msd, parallel, pmft
from .box import Box
from .locality import AABBQuery, LinkCell, NeighborList
from .parallel import NumThreads, get_num_threads, set_num_threads
Expand All @@ -17,7 +16,7 @@
__all__ = [
"__version__",
"box",
# "cluster",
"cluster",
"data",
# "density",
# "diffraction",
Expand Down
137 changes: 39 additions & 98 deletions freud/cluster.pyx → freud/cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,16 @@
of clusters of points in a system.
"""

from cython.operator cimport dereference

from freud.locality cimport _PairCompute
from freud.util cimport _Compute

import numpy as np

import freud._cluster
import freud.locality
import freud.util
from freud.locality import _PairCompute
from freud.util import _Compute

cimport numpy as np

cimport freud._cluster
cimport freud.locality
cimport freud.util

# numpy must be initialized. When using numpy from C or Cython you must
# _always_ do that, or you will have segfaults
np.import_array()

cdef class Cluster(_PairCompute):
class Cluster(_PairCompute):
"""Finds clusters using a network of neighbors.

Given a set of points and their neighbors, :class:`freud.cluster.Cluster`
Expand All @@ -50,16 +39,8 @@
:code:`cluster_keys` contains the point ids present in each cluster.
"""

cdef freud._cluster.Cluster * thisptr

def __cinit__(self):
self.thisptr = new freud._cluster.Cluster()

def __init__(self):
pass

def __dealloc__(self):
del self.thisptr
self._cpp_obj = freud._cluster.Cluster()

def compute(self, system, keys=None, neighbors=None):
r"""Compute the clusters for the given set of points.
Expand All @@ -77,52 +58,36 @@ def compute(self, system, keys=None, neighbors=None):
<https://freud.readthedocs.io/en/stable/topics/querying.html>`_
(Default value: None).
"""
cdef:
freud.locality.NeighborQuery nq
freud.locality.NeighborList nlist
freud.locality._QueryArgs qargs
const float[:, ::1] l_query_points
unsigned int num_query_points

nq, nlist, qargs, l_query_points, num_query_points = \
self._preprocess_arguments(system, neighbors=neighbors)

cdef unsigned int* l_keys_ptr = NULL
cdef unsigned int[::1] l_keys
nq, nlist, qargs, query_points, num_query_points = self._preprocess_arguments(
system, neighbors=neighbors
)
if keys is not None:
l_keys = freud.util._convert_array(
keys, shape=(num_query_points, ), dtype=np.uint32)
l_keys_ptr = &l_keys[0]

self.thisptr.compute(
nq.get_ptr(),
nlist.get_ptr(),
dereference(qargs.thisptr),
l_keys_ptr)
keys = freud.util._convert_array(
keys, shape=(num_query_points,), dtype=np.uint32
)

self._cpp_obj.compute(nq._cpp_obj, nlist._cpp_obj, qargs._cpp_obj, keys)
return self

@_Compute._computed_property
def num_clusters(self):
"""int: The number of clusters."""
return self.thisptr.getNumClusters()
return self._cpp_obj.getNumClusters()

@_Compute._computed_property
def cluster_idx(self):
"""(:math:`N_{points}`) :class:`numpy.ndarray`: The cluster index for
each point."""
return freud.util.make_managed_numpy_array(
&self.thisptr.getClusterIdx(),
freud.util.arr_type_t.UNSIGNED_INT)
return self._cpp_obj.getClusterIdx().toNumpyArray()

@_Compute._computed_property
def cluster_keys(self):
"""list(list): A list of lists of the keys contained in each
cluster."""
cluster_keys = self.thisptr.getClusterKeys()
return cluster_keys
return self._cpp_obj.getClusterKeys()

def __repr__(self):
return "freud.cluster.{cls}()".format(cls=type(self).__name__)
return f"freud.cluster.{type(self).__name__}()"

def plot(self, ax=None):
"""Plot cluster distribution.
Expand All @@ -136,22 +101,23 @@ def plot(self, ax=None):
(:class:`matplotlib.axes.Axes`): Axis with the plot.
"""
import freud.plot

try:
values, counts = np.unique(self.cluster_idx, return_counts=True)
except ValueError:
return None
return freud.plot.clusters_plot(
values, counts, num_clusters_to_plot=10, ax=ax)
return freud.plot.clusters_plot(values, counts, num_clusters_to_plot=10, ax=ax)

def _repr_png_(self):
try:
import freud.plot

return freud.plot._ax_to_bytes(self.plot())
except (AttributeError, ImportError):
return None


cdef class ClusterProperties(_Compute):
class ClusterProperties(_Compute):
r"""Routines for computing properties of point clusters.

Given a set of points and cluster ids (from :class:`~.Cluster` or another
Expand All @@ -169,16 +135,8 @@ def _repr_png_(self):
using the minimum image convention
"""

cdef freud._cluster.ClusterProperties * thisptr

def __cinit__(self):
self.thisptr = new freud._cluster.ClusterProperties()

def __init__(self):
pass

def __dealloc__(self):
del self.thisptr
self._cpp_obj = freud._cluster.ClusterProperties()

def compute(self, system, cluster_idx, masses=None):
r"""Compute properties of the point clusters.
Expand Down Expand Up @@ -210,21 +168,14 @@ def compute(self, system, cluster_idx, masses=None):
Masses corresponding to each point, defaulting to 1 if not
provided or :code:`None` (Default value = :code:`None`).
"""
cdef freud.locality.NeighborQuery nq = \
freud.locality.NeighborQuery.from_system(system)
cluster_idx = freud.util._convert_array(
cluster_idx, shape=(nq.points.shape[0], ), dtype=np.uint32)
cdef const unsigned int[::1] l_cluster_idx = cluster_idx
nq = freud.locality.NeighborQuery.from_system(system)

cdef float* l_masses_ptr = NULL
cdef float[::1] l_masses
cluster_idx = freud.util._convert_array(
cluster_idx, shape=(nq.points.shape[0],), dtype=np.uint32
)
if masses is not None:
l_masses = freud.util._convert_array(masses, shape=(len(masses), ))
l_masses_ptr = &l_masses[0]

self.thisptr.compute(nq.get_ptr(),
<unsigned int*> &l_cluster_idx[0],
l_masses_ptr)
masses = freud.util._convert_array(masses, shape=(len(masses),))
self._cpp_obj.compute(nq._cpp_obj, cluster_idx, masses)
return self

@_Compute._computed_property
Expand All @@ -240,9 +191,7 @@ def centers(self):
:math:`N_k` is the number of particles in the :math:`k` th cluster and
:math:`\mathbf{r_i}` are their positions.
"""
return freud.util.make_managed_numpy_array(
&self.thisptr.getClusterCenters(),
freud.util.arr_type_t.FLOAT, 3)
return self._cpp_obj.getClusterCenters().toNumpyArray()

@_Compute._computed_property
def centers_of_mass(self):
Expand All @@ -258,9 +207,7 @@ def centers_of_mass(self):
cluster, :math:`\mathbf{r_i}` are their positions and :math:`m_i` are
their masses.
"""
return freud.util.make_managed_numpy_array(
&self.thisptr.getClusterCentersOfMass(),
freud.util.arr_type_t.FLOAT, 3)
return self._cpp_obj.getClusterCentersOfMass().toNumpyArray()

@_Compute._computed_property
def gyrations(self):
Expand All @@ -278,9 +225,7 @@ def gyrations(self):
where :math:`\mathbf{S}_k` is the gyration tensor of the :math:`k` th
cluster.
"""
return freud.util.make_managed_numpy_array(
&self.thisptr.getClusterGyrations(),
freud.util.arr_type_t.FLOAT)
return self._cpp_obj.getClusterGyrations().toNumpyArray()

@_Compute._computed_property
def inertia_tensors(self):
Expand All @@ -298,25 +243,19 @@ def inertia_tensors(self):
where :math:`\mathbf{I}_k` is the inertia tensor of the :math:`k` th
cluster.
"""
return freud.util.make_managed_numpy_array(
&self.thisptr.getClusterMomentsOfInertia(),
freud.util.arr_type_t.FLOAT)
return self._cpp_obj.getClusterMomentsOfInertia().toNumpyArray()

@_Compute._computed_property
def sizes(self):
"""(:math:`N_{clusters}`) :class:`numpy.ndarray`: The cluster sizes."""
return freud.util.make_managed_numpy_array(
&self.thisptr.getClusterSizes(),
freud.util.arr_type_t.UNSIGNED_INT)
return self._cpp_obj.getClusterSizes().toNumpyArray()

@_Compute._computed_property
def cluster_masses(self):
"""(:math:`N_{clusters}`) :class:`numpy.ndarray`: The total mass of
particles in each cluster.
"""
return freud.util.make_managed_numpy_array(
&self.thisptr.getClusterMasses(),
freud.util.arr_type_t.FLOAT)
return self._cpp_obj.getClusterMasses().toNumpyArray()

@_Compute._computed_property
def radii_of_gyration(self):
Expand All @@ -331,8 +270,10 @@ def radii_of_gyration(self):
the center of mass.

"""
return np.sqrt(np.trace(self.inertia_tensors, axis1=-2, axis2=-1)
/(2*self.cluster_masses))
return np.sqrt(
np.trace(self.inertia_tensors, axis1=-2, axis2=-1)
/ (2 * self.cluster_masses)
)

def __repr__(self):
return "freud.cluster.{cls}()".format(cls=type(self).__name__)
return f"freud.cluster.{type(self).__name__}()"
9 changes: 5 additions & 4 deletions freud/cluster/Cluster.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@
//! Finds clusters using a network of neighbors.
namespace freud { namespace cluster {

void Cluster::compute(const freud::locality::NeighborQuery* nq, const freud::locality::NeighborList* nlist,
freud::locality::QueryArgs qargs, const unsigned int* keys)
void Cluster::compute(const std::shared_ptr<locality::NeighborQuery> nq,
const std::shared_ptr<locality::NeighborList> nlist, const locality::QueryArgs& qargs,
const unsigned int* keys)
{
const unsigned int num_points = nq->getNPoints();
m_cluster_idx.prepare(num_points);
m_cluster_idx = std::make_shared<util::ManagedArray<unsigned int>>(num_points);
DisjointSets dj(num_points);

freud::locality::loopOverNeighbors(
Expand Down Expand Up @@ -80,7 +81,7 @@ void Cluster::compute(const freud::locality::NeighborQuery* nq, const freud::loc
{
size_t s = dj.find(i);
size_t cluster_idx = cluster_reindex[cluster_label[s]];
m_cluster_idx[i] = cluster_idx;
(*m_cluster_idx)[i] = cluster_idx;
unsigned int key = i;
if (keys != nullptr)
{
Expand Down
Loading
Loading