Skip to content

Commit

Permalink
Fixed ClusterProperties, added util._convert_array to all arrays, cha…
Browse files Browse the repository at this point in the history
…nged export file so that nbarrays have const arguments.
  • Loading branch information
tfteague committed Sep 16, 2024
1 parent e730ce9 commit d114b7d
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 199 deletions.
361 changes: 173 additions & 188 deletions freud/cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
The :class:`freud.cluster` module aids in finding and computing the properties
of clusters of points in a system.
"""
import numpy as np
from freud.locality import _PairCompute
from freud.util import _Compute

Expand Down Expand Up @@ -57,7 +58,11 @@ def compute(self, system, keys=None, neighbors=None):
nq, nlist, qargs, query_points, num_query_points = self._preprocess_arguments(
system, neighbors=neighbors
)
print(type(nq),type(nlist),type(qargs._cpp_obj),type(keys))
if keys is not None:
keys = freud.util._convert_array(
keys, shape = (num_query_points, ), dtype = np.uint32
)

self._cpp_obj.compute(
nq._cpp_obj,
nlist._cpp_obj,
Expand All @@ -68,19 +73,19 @@ def compute(self, system, keys=None, neighbors=None):
@_Compute._computed_property
def num_clusters(self):
"""in0t: The number of clusters."""
return self.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 self.getClusterIdx().toNumpyArray()
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.getClusterKeys()
cluster_keys = self._cpp_obj.getClusterKeys()
return cluster_keys

def __repr__(self):
Expand Down Expand Up @@ -113,187 +118,167 @@ def _repr_png_(self):
return None


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

# Given a set of points and cluster ids (from :class:`~.Cluster` or another
# source), this class determines the following properties for each cluster:

# - Geometric center
# - Center of mass
# - Gyration tensor
# - Moment of
# inertia tensor
# - Size (number of points)
# - Mass (total mass of each cluster)

# Note:
# The center of mass and geometric center for each cluster are computed
# using the minimum image convention
# """

# def freud._cluster.ClusterProperties * _cpp_obj

# def __init__(self):
# self._cpp_obj = freud._cluster.ClusterProperties()

# def __init__(self):
# pass


# def compute(self, system, cluster_idx, masses=None):
# r"""Compute properties of the point clusters.
# Loops over all points in the given array and determines the geometric
# center, center of mass, moment of inertia, gyration tensors, and
# radius of gyration :cite:`Vymetal2011` of each cluster. After calling
# this method, properties can be accessed via their attributes.

# Example::

# >>> import freud
# >>> # Compute clusters using box, positions, and nlist data
# >>> box, points = freud.data.make_random_system(10, 100)
# >>> cl = freud.cluster.Cluster()
# >>> cl.compute((box, points), neighbors={'r_max': 1.0})
# freud.cluster.Cluster()
# >>> # Compute cluster properties based on identified clusters
# >>> cl_props = freud.cluster.ClusterProperties()
# >>> cl_props.compute((box, points), cl.cluster_idx)
# freud.cluster.ClusterProperties()

# Args:
# system:
# Any object that is a valid argument to
# :class:`freud.locality.NeighborQuery.from_system`.
# cluster_idx ((:math:`N_{points}`,) :class:`np.ndarray`):
# Cluster indexes for each point.
# masses ((:math:`N_{points}`, ) :class:`numpy.ndarray`):
# Masses corresponding to each point, defaulting to 1 if not
# provided or :code:`None` (Default value = :code:`None`).
# """
# def 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)
# def const unsigned int[::1] l_cluster_idx = cluster_idx

# def float* l_masses_ptr = NULL
# def float[::1] l_masses
# if masses is not None:
# l_masses = freud.util._convert_array(masses, shape=(len(masses), ))
# l_masses_ptr = &l_masses[0]

# self._cpp_obj.compute(nq.get_ptr(),
# <unsigned int*> &l_cluster_idx[0],
# l_masses_ptr)
# return self

# @_Compute._computed_property
# def centers(self):
# r"""(:math:`N_{clusters}`, 3) :class:`numpy.ndarray`: The geometric centers
# of the clusters, independent of mass and defined as

# .. math::

# \mathbf{C}_g^k = \frac{1}{N_k} \sum_{i=0}^{N_k} \mathbf{r_i}

# where :math:`\mathbf{C}_g^k` is the center of the :math:`k` th cluster,
# :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._cpp_obj.getClusterCenters(),
# freud.util.arr_type_t.FLOAT, 3)

# @_Compute._computed_property
# def centers_of_mass(self):
# r"""(:math:`N_{clusters}`, 3) :class:`numpy.ndarray`: The centers of mass
# of the clusters:

# .. math::

# \mathbf{C}_m^k = \frac{1}{M_k} \sum_{i=0}^{N_k} m_i \mathbf{r_i}

# where :math:`\mathbf{C}_m^k` is the center of mass of the :math:`k` th
# cluster, :math:`M_k` is the total mass of particles in the :math:`k` th
# cluster, :math:`\mathbf{r_i}` are their positions and :math:`m_i` are
# their masses.
# """
# return freud.util.make_managed_numpy_array(
# &self._cpp_obj.getClusterCentersOfMass(),
# freud.util.arr_type_t.FLOAT, 3)

# @_Compute._computed_property
# def gyrations(self):
# r"""(:math:`N_{clusters}`, 3, 3) :class:`numpy.ndarray`: The gyration
# tensors of the clusters. Normalized by particle number:

# .. math::

# \mathbf{S}_k = \frac{1}{N_k} \begin{bmatrix}
# \sum_i x_i^2 & \sum_i x_i y_i & \sum_i x_i z_i \\
# \sum_i y_i x_i & \sum_i y_i^2 & \sum_i y_i z_i \\
# \sum_i z_i x_i & \sum_i z_i y_i & \sum_i z_i^2 \\
# \end{bmatrix}

# where :math:`\mathbf{S}_k` is the gyration tensor of the :math:`k` th
# cluster.
# """
# return freud.util.make_managed_numpy_array(
# &self._cpp_obj.getClusterGyrations(),
# freud.util.arr_type_t.FLOAT)

# @_Compute._computed_property
# def inertia_tensors(self):
# r"""(:math:`N_{clusters}`, 3, 3) :class:`numpy.ndarray`: The inertia
# tensors of the clusters. Neither normalized by mass nor number:

# .. math::

# \mathbf{I}_k = \begin{bmatrix}
# \sum_i m_i(y_i^2+z_i^2)& \sum_i -m_i(x_iy_i)& \sum_i -m_i(x_iz_i)\\
# \sum_i -m_i(y_ix_i)& \sum_i m_i(x_i^2+z_i^2)& \sum_i -m_i(y_iz_i)\\
# \sum_i -m_i(z_ix_i)& \sum_i -m_i(z_iy_i)& \sum_i m_i(y_i^2+x_i^2)\\
# \end{bmatrix}

# where :math:`\mathbf{I}_k` is the inertia tensor of the :math:`k` th
# cluster.
# """
# return freud.util.make_managed_numpy_array(
# &self._cpp_obj.getClusterMomentsOfInertia(),
# freud.util.arr_type_t.FLOAT)

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

# @_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._cpp_obj.getClusterMasses(),
# freud.util.arr_type_t.FLOAT)

# @_Compute._computed_property
# def radii_of_gyration(self):
# r"""(:math:`N_{clusters}`,) :class:`numpy.ndarray`: The radius of
# gyration of each cluster. Defined by IUPAP as

# .. math::

# R_g^k = \left(\frac{1}{M} \sum_{i=0}^{N_k} m_i s_i^2 \right)^{1/2}

# where :math:`s_i` is the distance of particle :math:`i` from
# the center of mass.

# """
# 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__)
class ClusterProperties(_Compute):
r"""Routines for computing properties of point clusters.
Given a set of points and cluster ids (from :class:`~.Cluster` or another
source), this class determines the following properties for each cluster:
- Geometric center
- Center of mass
- Gyration tensor
- Moment of
inertia tensor
- Size (number of points)
- Mass (total mass of each cluster)
Note:
The center of mass and geometric center for each cluster are computed
using the minimum image convention
"""

def __init__(self):
self._cpp_obj = freud._cluster.ClusterProperties()

def compute(self, system, cluster_idx, masses=None):
r"""Compute properties of the point clusters.
Loops over all points in the given array and determines the geometric
center, center of mass, moment of inertia, gyration tensors, and
radius of gyration :cite:`Vymetal2011` of each cluster. After calling
this method, properties can be accessed via their attributes.
Example::
>>> import freud
>>> # Compute clusters using box, positions, and nlist data
>>> box, points = freud.data.make_random_system(10, 100)
>>> cl = freud.cluster.Cluster()
>>> cl.compute((box, points), neighbors={'r_max': 1.0})
freud.cluster.Cluster()
>>> # Compute cluster properties based on identified clusters
>>> cl_props = freud.cluster.ClusterProperties()
>>> cl_props.compute((box, points), cl.cluster_idx)
freud.cluster.ClusterProperties()
Args:
system:
Any object that is a valid argument to
:class:`freud.locality.NeighborQuery.from_system`.
cluster_idx ((:math:`N_{points}`,) :class:`np.ndarray`):
Cluster indexes for each point.
masses ((:math:`N_{points}`, ) :class:`numpy.ndarray`):
Masses corresponding to each point, defaulting to 1 if not
provided or :code:`None` (Default value = :code:`None`).
"""
nq = freud.locality.NeighborQuery.from_system(system)

cluster_idx = freud.util._convert_array(
cluster_idx, shape = (nq.points.shape[0], ), dtype = np.uint32
)
if masses is not None:
masses = freud.util._convert_array(masses, shape = (len(masses), )
)
print(type(self._cpp_obj), type(nq._cpp_obj), type(cluster_idx), type(masses))
print(cluster_idx,cluster_idx.shape, cluster_idx.dtype)
self._cpp_obj.compute(nq._cpp_obj,
cluster_idx,
masses)
return self

@_Compute._computed_property
def centers(self):
r"""(:math:`N_{clusters}`, 3) :class:`numpy.ndarray`: The geometric centers
of the clusters, independent of mass and defined as
.. math::
\mathbf{C}_g^k = \frac{1}{N_k} \sum_{i=0}^{N_k} \mathbf{r_i}
where :math:`\mathbf{C}_g^k` is the center of the :math:`k` th cluster,
:math:`N_k` is the number of particles in the :math:`k` th cluster and
:math:`\mathbf{r_i}` are their positions.
"""
return self._cpp_obj.getClusterCenters().toNumpyArray()

@_Compute._computed_property
def centers_of_mass(self):
r"""(:math:`N_{clusters}`, 3) :class:`numpy.ndarray`: The centers of mass
of the clusters:
.. math::
\mathbf{C}_m^k = \frac{1}{M_k} \sum_{i=0}^{N_k} m_i \mathbf{r_i}
where :math:`\mathbf{C}_m^k` is the center of mass of the :math:`k` th
cluster, :math:`M_k` is the total mass of particles in the :math:`k` th
cluster, :math:`\mathbf{r_i}` are their positions and :math:`m_i` are
their masses.
"""
return self._cpp_obj.getClusterCentersOfMass().toNumpyArray()

@_Compute._computed_property
def gyrations(self):
r"""(:math:`N_{clusters}`, 3, 3) :class:`numpy.ndarray`: The gyration
tensors of the clusters. Normalized by particle number:
.. math::
\mathbf{S}_k = \frac{1}{N_k} \begin{bmatrix}
\sum_i x_i^2 & \sum_i x_i y_i & \sum_i x_i z_i \\
\sum_i y_i x_i & \sum_i y_i^2 & \sum_i y_i z_i \\
\sum_i z_i x_i & \sum_i z_i y_i & \sum_i z_i^2 \\
\end{bmatrix}
where :math:`\mathbf{S}_k` is the gyration tensor of the :math:`k` th
cluster.
"""
return self._cpp_obj.getClusterGyrations().toNumpyArray()

@_Compute._computed_property
def inertia_tensors(self):
r"""(:math:`N_{clusters}`, 3, 3) :class:`numpy.ndarray`: The inertia
tensors of the clusters. Neither normalized by mass nor number:
.. math::
\mathbf{I}_k = \begin{bmatrix}
\sum_i m_i(y_i^2+z_i^2)& \sum_i -m_i(x_iy_i)& \sum_i -m_i(x_iz_i)\\
\sum_i -m_i(y_ix_i)& \sum_i m_i(x_i^2+z_i^2)& \sum_i -m_i(y_iz_i)\\
\sum_i -m_i(z_ix_i)& \sum_i -m_i(z_iy_i)& \sum_i m_i(y_i^2+x_i^2)\\
\end{bmatrix}
where :math:`\mathbf{I}_k` is the inertia tensor of the :math:`k` th
cluster.
"""
return self._cpp_obj.getClusterMomentsOfInertia().toNumpyArray()

@_Compute._computed_property
def sizes(self):
"""(:math:`N_{clusters}`) :class:`numpy.ndarray`: The cluster sizes."""
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 self._cpp_obj.getClusterMasses().toNumpyArray()

@_Compute._computed_property
def radii_of_gyration(self):
r"""(:math:`N_{clusters}`,) :class:`numpy.ndarray`: The radius of
gyration of each cluster. Defined by IUPAP as
.. math::
R_g^k = \left(\frac{1}{M} \sum_{i=0}^{N_k} m_i s_i^2 \right)^{1/2}
where :math:`s_i` is the distance of particle :math:`i` from
the center of mass.
"""
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__)
Loading

0 comments on commit d114b7d

Please sign in to comment.