diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 17ba73ba7..835fd477c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -65,6 +65,7 @@ jobs: pytest tests/test_data.py -v pytest tests/test_pmft.py -v pytest tests/test_util.py -v + pytest tests/test_environment*.py -v pytest tests/test_diffraction*.py -v pytest tests/test_interface.py -v pytest tests/test_msd_msd.py -v diff --git a/freud/CMakeLists.txt b/freud/CMakeLists.txt index 08711d4bd..86a97011f 100644 --- a/freud/CMakeLists.txt +++ b/freud/CMakeLists.txt @@ -42,17 +42,17 @@ add_library( diffraction/StaticStructureFactorDirect.h diffraction/StaticStructureFactorDirect.cc # environment - # environment/AngularSeparation.h - # environment/AngularSeparation.cc - # environment/BondOrder.h - # environment/BondOrder.cc - # environment/LocalBondProjection.h - # environment/LocalBondProjection.cc - # environment/LocalDescriptors.h - # environment/LocalDescriptors.cc - # environment/MatchEnv.h - # environment/MatchEnv.cc - # environment/Registration.h + environment/AngularSeparation.h + environment/AngularSeparation.cc + environment/BondOrder.h + environment/BondOrder.cc + environment/LocalBondProjection.h + environment/LocalBondProjection.cc + environment/LocalDescriptors.h + environment/LocalDescriptors.cc + environment/MatchEnv.h + environment/MatchEnv.cc + environment/Registration.h # locality locality/AABB.h locality/AABBQuery.cc @@ -152,15 +152,26 @@ nanobind_add_module( target_link_libraries(_diffraction PUBLIC freud TBB::tbb) target_set_install_rpath(_diffraction) -# environment nanobind_add_module(_environment environment/...) -# target_link_libraries(_environment PUBLIC freud TBB::tbb) -# target_set_install_rpath(_environment) - # box nanobind_add_module(_box box/module-box.cc box/export-Box.cc box/export-Box.h) target_link_libraries(_box PUBLIC TBB::tbb) target_set_install_rpath(_box) +# environment +nanobind_add_module( + _environment + environment/module-environment.cc + environment/export-AngularSeparationNeighbor.cc + environment/export-AngularSeparationGlobal.cc + environment/export-LocalBondProjection.cc + environment/export-LocalDescriptors.cc + environment/export-BondOrder.cc + environment/export-MatchEnv.cc +) +target_link_libraries(_environment PUBLIC freud TBB::tbb) +target_set_install_rpath(_environment) + + # locality nanobind_add_module( _locality @@ -213,6 +224,7 @@ set(python_files box.py cluster.py data.py + environment.py diffraction.py errors.py locality.py @@ -223,7 +235,7 @@ set(python_files interface.py plot.py util.py) -# cluster.py density.py diffraction.py environment.py interface.py msd.py) +# density.py) copy_files_to_build("${python_files}" "freud" "*.py") @@ -232,9 +244,9 @@ 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 _environment DESTINATION freud) install(TARGETS _diffraction DESTINATION freud) - # install(TARGETS _density DESTINATION freud) install(TARGETS _environment - # DESTINATION freud) install(TARGETS _locality DESTINATION freud) install(TARGETS _order DESTINATION freud) install(TARGETS _parallel DESTINATION freud) diff --git a/freud/__init__.py b/freud/__init__.py index 618852f3c..9a8b67dda 100644 --- a/freud/__init__.py +++ b/freud/__init__.py @@ -1,11 +1,13 @@ # 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. +# density, from . import ( box, cluster, data, diffraction, + environment, interface, locality, msd, @@ -30,7 +32,7 @@ "data", # "density", "diffraction", - # "environment", + "environment", "interface", "locality", "msd", diff --git a/freud/environment.pyx b/freud/environment.py similarity index 71% rename from freud/environment.pyx rename to freud/environment.py index d7a67fb10..74964f93b 100644 --- a/freud/environment.pyx +++ b/freud/environment.py @@ -8,33 +8,184 @@ characterize the particle environment. """ +import collections.abc +import warnings + +import numpy as np + +import freud._environment +import freud.box +import freud.locality +import freud.util +from freud._util import ( # noqa F401 + ManagedArray_double, + ManagedArray_float, + ManagedArray_unsignedint, + ManagedArrayVec3_float, + Vector_double, + Vector_float, + Vector_unsignedint, + VectorVec3_float, +) from freud.errors import NO_DEFAULT_QUERY_ARGS_MESSAGE +from freud.locality import _Compute, _PairCompute, _SpatialHistogram -from cython.operator cimport dereference -from libcpp.map cimport map -from freud.locality cimport _PairCompute, _SpatialHistogram -from freud.util cimport _Compute, quat, vec3 +class AngularSeparationNeighbor(_PairCompute): + r"""Calculates the minimum angles of separation between orientations and + query orientations.""" -import warnings + def __init__(self): + self._cpp_obj = freud._environment.AngularSeparationNeighbor() + + def compute( + self, + system, + orientations, + query_points=None, + query_orientations=None, + equiv_orientations=np.array([[1, 0, 0, 0]]), + neighbors=None, + ): + r"""Calculates the minimum angles of separation between :code:`orientations` + and :code:`query_orientations`, checking for underlying symmetry as encoded + in :code:`equiv_orientations`. The result is stored in the :code:`neighbor_angles` + class attribute. -import numpy as np + Args: + system: + Any object that is a valid argument to + :class:`freud.locality.NeighborQuery.from_system`. + orientations ((:math:`N_{points}`, 4) :class:`numpy.ndarray`): + Orientations associated with system points that are used to + calculate bonds. + query_points ((:math:`N_{query\_points}`, 3) :class:`numpy.ndarray`, optional): + Query points used to calculate the correlation function. Uses + the system's points if :code:`None` (Default + value = :code:`None`). + query_orientations ((:math:`N_{query\_points}`, 4) :class:`numpy.ndarray`, optional): + Query orientations used to calculate bonds. Uses + :code:`orientations` if :code:`None`. (Default + value = :code:`None`). + equiv_orientations ((:math:`N_{equiv}`, 4) :class:`numpy.ndarray`, optional): + The set of all equivalent quaternions that map the particle + to itself (the elements of its rotational symmetry group). + Important: :code:`equiv_orientations` must include both + :math:`q` and :math:`-q`, for all included quaternions. Note + that this calculation assumes that all points in the system + share the same set of equivalent orientations. + (Default value = :code:`[[1, 0, 0, 0]]`) + neighbors (:class:`freud.locality.NeighborList` or dict, optional): + Either a :class:`NeighborList ` of + neighbor pairs to use in the calculation, or a dictionary of + `query arguments + `_ + (Default value: None). + """ # noqa: E501 -import freud.locality + nq, nlist, qargs, query_points, num_query_points = self._preprocess_arguments( + system, query_points, neighbors + ) + + orientations = freud.util._convert_array( + orientations, shape=(nq.points.shape[0], 4) + ) + if query_orientations is None: + query_orientations = orientations + else: + query_orientations = freud.util._convert_array( + query_orientations, shape=(query_points.shape[0], 4) + ) + + equiv_orientations = freud.util._convert_array( + equiv_orientations, shape=(None, 4) + ) + + self._cpp_obj.compute( + nq._cpp_obj, + orientations, + query_points, + query_orientations, + equiv_orientations, + nlist._cpp_obj, + qargs._cpp_obj, + ) + return self + + @_Compute._computed_property + def angles(self): + """:math:`\\left(N_{bonds}\\right)` :class:`numpy.ndarray`: The + neighbor angles in radians. The angles are stored in the order of the + neighborlist object.""" + return self._cpp_obj.getAngles().toNumpyArray() + + def __repr__(self): + return f"freud.environment.{type(self).__name__}()" + + @_Compute._computed_property + def nlist(self): + """:class:`freud.locality.NeighborList`: The neighbor list from the + last compute.""" + nlist = freud.locality._nlist_from_cnlist(self._cpp_obj.getNList()) + nlist._compute = self + return nlist -cimport numpy as np -cimport freud._environment -cimport freud.box -cimport freud.locality -cimport freud.util +class AngularSeparationGlobal(_Compute): + r"""Calculates the minimum angles of separation between orientations and + global orientations.""" -# numpy must be initialized. When using numpy from C or Cython you must -# _always_ do that, or you will have segfaults -np.import_array() + def __init__(self): + self._cpp_obj = freud._environment.AngularSeparationGlobal() + + def compute( + self, + global_orientations, + orientations, + equiv_orientations=np.array([[1, 0, 0, 0]]), + ): + r"""Calculates the minimum angles of separation between + :code:`global_orientations` and :code:`orientations`, checking for + underlying symmetry as encoded in :code:`equiv_orientations`. The + result is stored in the :code:`global_angles` class attribute. + Args: + global_orientations ((:math:`N_{global}`, 4) :class:`numpy.ndarray`): + Set of global orientations to calculate the order + parameter. + orientations ((:math:`N_{particles}`, 4) :class:`numpy.ndarray`): + Orientations to calculate the order parameter. + equiv_orientations ((:math:`N_{equiv}`, 4) :class:`numpy.ndarray`, optional): + The set of all equivalent quaternions that map the particle + to itself (the elements of its rotational symmetry group). + Important: :code:`equiv_orientations` must include both + :math:`q` and :math:`-q`, for all included quaternions. Note + that this calculation assumes that all points in the system + share the same set of equivalent orientations. + (Default value = :code:`[[1, 0, 0, 0]]`) + """ # noqa: E501 + global_orientations = freud.util._convert_array( + global_orientations, shape=(None, 4) + ) + orientations = freud.util._convert_array(orientations, shape=(None, 4)) + equiv_orientations = freud.util._convert_array( + equiv_orientations, shape=(None, 4) + ) + + self._cpp_obj.compute(global_orientations, orientations, equiv_orientations) + return self -cdef class BondOrder(_SpatialHistogram): + @_Compute._computed_property + def angles(self): + """:math:`\\left(N_{orientations}, N_{global\\_orientations}\\right)` :class:`numpy.ndarray`: + The global angles in radians.""" # noqa: E501 + return self._cpp_obj.getAngles().toNumpyArray() + + def __repr__(self): + return f"freud.environment.{type(self).__name__}()" + + +class BondOrder(_SpatialHistogram): r"""Compute the bond orientational order diagram for the system of particles. @@ -101,42 +252,46 @@ Mode to calculate bond order. Options are :code:`'bod'`, :code:`'lbod'`, :code:`'obcd'`, or :code:`'oocd'` (Default value = :code:`'bod'`). - """ # noqa: E501 - cdef freud._environment.BondOrder * thisptr + """ - known_modes = {'bod': freud._environment.bod, - 'lbod': freud._environment.lbod, - 'obcd': freud._environment.obcd, - 'oocd': freud._environment.oocd} + known_modes = { + "bod": freud._environment.bod, + "lbod": freud._environment.lbod, + "obcd": freud._environment.obcd, + "oocd": freud._environment.oocd, + } - def __cinit__(self, bins, str mode="bod"): - try: + def __init__(self, bins, mode="bod"): + if isinstance(bins, collections.abc.Sequence): n_bins_theta, n_bins_phi = bins - except TypeError: + else: n_bins_theta = n_bins_phi = bins - cdef freud._environment.BondOrderMode l_mode try: l_mode = self.known_modes[mode] - except KeyError: - raise ValueError( - 'Unknown BondOrder mode: {}'.format(mode)) - - self.thisptr = self.histptr = new freud._environment.BondOrder( - n_bins_theta, n_bins_phi, l_mode) + except KeyError as err: + msg = f"Unknown BondOrder mode: {mode}" + raise ValueError(msg) from err - def __dealloc__(self): - del self.thisptr + self._cpp_obj = freud._environment.BondOrder(n_bins_theta, n_bins_phi, l_mode) @property def default_query_args(self): """No default query arguments.""" # Must override the generic histogram's defaults. raise NotImplementedError( - NO_DEFAULT_QUERY_ARGS_MESSAGE.format(type(self).__name__)) - - def compute(self, system, orientations=None, query_points=None, - query_orientations=None, neighbors=None, reset=True): + NO_DEFAULT_QUERY_ARGS_MESSAGE.format(type(self).__name__) + ) + + def compute( + self, + system, + orientations=None, + query_points=None, + query_orientations=None, + neighbors=None, + reset=True, + ): r"""Calculates the correlation function and adds to the current histogram. @@ -166,69 +321,64 @@ def compute(self, system, orientations=None, query_points=None, Whether to erase the previously computed values before adding the new computation; if False, will accumulate data (Default value: True). - """ # noqa: E501 + """ if reset: self._reset() - 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, query_points, neighbors) + nq, nlist, qargs, l_query_points, num_query_points = self._preprocess_arguments( + system, query_points, neighbors + ) if orientations is None: orientations = np.array([[1, 0, 0, 0]] * nq.points.shape[0]) if query_orientations is None: query_orientations = orientations + if query_points is None: + query_points = nq.points orientations = freud.util._convert_array( - orientations, shape=(nq.points.shape[0], 4)) + orientations, shape=(nq.points.shape[0], 4) + ) query_orientations = freud.util._convert_array( - query_orientations, shape=(num_query_points, 4)) - - cdef const float[:, ::1] l_orientations = orientations - cdef const float[:, ::1] l_query_orientations = query_orientations - - self.thisptr.accumulate( - nq.get_ptr(), - &l_orientations[0, 0], - &l_query_points[0, 0], - &l_query_orientations[0, 0], - num_query_points, - nlist.get_ptr(), dereference(qargs.thisptr)) + query_orientations, shape=(num_query_points, 4) + ) + query_points = freud.util._convert_array( + query_points, shape=(num_query_points, 3) + ) + + self._cpp_obj.accumulate( + nq._cpp_obj, + orientations, + query_points, + query_orientations, + nlist._cpp_obj, + qargs._cpp_obj, + ) return self @_Compute._computed_property def bond_order(self): """:math:`\\left(N_{\\phi}, N_{\\theta} \\right)` :class:`numpy.ndarray`: Bond order.""" # noqa: E501 - return freud.util.make_managed_numpy_array( - &self.thisptr.getBondOrder(), - freud.util.arr_type_t.FLOAT) + return self._cpp_obj.getBondOrder().toNumpyArray() @_Compute._computed_property def box(self): """:class:`freud.box.Box`: Box used in the calculation.""" - return freud.box.BoxFromCPP(self.thisptr.getBox()) + return freud.box.BoxFromCPP(self._cpp_obj.getBox()) def __repr__(self): - return ("freud.environment.{cls}(bins=({bins}), mode='{mode}')".format( + return "freud.environment.{cls}(bins=({bins}), mode='{mode}')".format( cls=type(self).__name__, - bins=', '.join([str(b) for b in self.nbins]), - mode=self.mode)) + bins=", ".join([str(b) for b in self.nbins]), + mode=self.mode, + ) @property def mode(self): """str: Bond order mode.""" - mode = self.thisptr.getMode() - for key, value in self.known_modes.items(): - if value == mode: - return key + return self._cpp_obj.getMode().name -cdef class LocalDescriptors(_PairCompute): +class LocalDescriptors(_PairCompute): r"""Compute a set of descriptors (a numerical "fingerprint") of a particle's local environment. @@ -256,26 +406,20 @@ def mode(self): neighborhood, :code:`'particle_local'` to use the given particle orientations, or :code:`'global'` to not rotate environments (Default value = :code:`'neighborhood'`). - """ # noqa: E501 - cdef freud._environment.LocalDescriptors * thisptr + """ known_modes = {'neighborhood': freud._environment.LocalNeighborhood, 'global': freud._environment.Global, 'particle_local': freud._environment.ParticleLocal} - def __cinit__(self, l_max, negative_m=True, mode='neighborhood'): - cdef freud._environment.LocalDescriptorOrientation l_mode + def __init__(self, l_max, negative_m=True, mode='neighborhood'): try: l_mode = self.known_modes[mode] except KeyError: - raise ValueError( - 'Unknown LocalDescriptors orientation mode: {}'.format(mode)) - - self.thisptr = new freud._environment.LocalDescriptors( - l_max, negative_m, l_mode) + msg = f'Unknown LocalDescriptors orientation mode: {mode}' + raise ValueError(msg) from None - def __dealloc__(self): - del self.thisptr + self._cpp_obj = freud._environment.LocalDescriptors(l_max, negative_m, l_mode) def compute(self, system, query_points=None, orientations=None, neighbors=None, max_num_neighbors=0): @@ -304,44 +448,37 @@ def compute(self, system, query_points=None, orientations=None, particle for the given neighbor-finding algorithm. Uses all neighbors if set to 0 (Default value = 0). """ # noqa: E501 - 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 = \ + nq, nlist, qargs, query_points, num_query_points = \ self._preprocess_arguments(system, query_points, neighbors) # The l_orientations_ptr is only used for 'particle_local' mode. - cdef const float[:, ::1] l_orientations - cdef quat[float] *l_orientations_ptr = NULL if self.mode == 'particle_local': if orientations is None: - raise RuntimeError( - ('Orientations must be given to orient LocalDescriptors ' - 'with particles\' orientations')) + msg = ( + 'Orientations must be given to orient LocalDescriptors ' + 'with particles\' orientations' + ) + raise RuntimeError(msg) orientations = freud.util._convert_array( orientations, shape=(nq.points.shape[0], 4)) - l_orientations = orientations - l_orientations_ptr = &l_orientations[0, 0] - - self.thisptr.compute( - nq.get_ptr(), - &l_query_points[0, 0], num_query_points, - l_orientations_ptr, - nlist.get_ptr(), dereference(qargs.thisptr), - max_num_neighbors) + self._cpp_obj.compute( + nq._cpp_obj, + query_points, + num_query_points, + orientations, + nlist._cpp_obj, + qargs._cpp_obj, + max_num_neighbors + ) return self @_Compute._computed_property def nlist(self): """:class:`freud.locality.NeighborList`: The neighbor list from the last compute.""" - nlist = freud.locality._nlist_from_cnlist(self.thisptr.getNList()) + nlist = freud.locality._nlist_from_cnlist(self._cpp_obj.getNList()) nlist._compute = self return nlist @@ -349,9 +486,7 @@ def nlist(self): def sph(self): """:math:`\\left(N_{bonds}, \\text{SphWidth} \\right)` :class:`numpy.ndarray`: The last computed spherical harmonic array.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getSph(), - freud.util.arr_type_t.COMPLEX_FLOAT) + return self._cpp_obj.getSph().toNumpyArray() @_Compute._computed_property def num_sphs(self): @@ -361,19 +496,19 @@ def num_sphs(self): :code:`num_neighbors` arguments passed to the last compute call or the constructor (it may be less if there are not enough neighbors for every particle).""" - return self.thisptr.getNSphs() + return self._cpp_obj.getNSphs() @property def l_max(self): """unsigned int: The maximum spherical harmonic :math:`l` calculated for.""" - return self.thisptr.getLMax() + return self._cpp_obj.getLMax() @property def negative_m(self): """bool: True if we also calculated :math:`Y_{lm}` for negative :math:`m`.""" - return self.thisptr.getNegativeM() + return self._cpp_obj.getNegativeM() @property def mode(self): @@ -381,16 +516,15 @@ def mode(self): :code:`'neighborhood'` to use the orientation of the local neighborhood, :code:`'particle_local'` to use the given particle orientations, or :code:`'global'` to not rotate environments.""" - mode = self.thisptr.getMode() + mode = self._cpp_obj.getMode() for key, value in self.known_modes.items(): if value == mode: return key + return None def __repr__(self): - return ("freud.environment.{cls}(l_max={l_max}, " - "negative_m={negative_m}, mode='{mode}')").format( - cls=type(self).__name__, l_max=self.l_max, - negative_m=self.negative_m, mode=self.mode) + return (f"freud.environment.{type(self).__name__}(l_max={self.l_max}, " + f"negative_m={self.negative_m}, mode='{self.mode}')") def _minimize_RMSD(box, ref_points, points, registration=False): @@ -414,29 +548,29 @@ def _minimize_RMSD(box, ref_points, points, registration=False): set of points, and the mapping between the vectors of ref_points and points that somewhat minimizes the RMSD. """ # noqa: E501 - cdef freud.box.Box b = freud.util._convert_box(box) + b = freud.util._convert_box(box) ref_points = freud.util._convert_array(ref_points, shape=(None, 3)) points = freud.util._convert_array(points, shape=(None, 3)) - cdef const float[:, ::1] l_ref_points = ref_points - cdef const float[:, ::1] l_points = points - cdef unsigned int nRef1 = l_ref_points.shape[0] - cdef unsigned int nRef2 = l_points.shape[0] + nRef1 = ref_points.shape[0] + nRef2 = points.shape[0] if nRef1 != nRef2: - raise ValueError( - ("The number of vectors in ref_points must MATCH" - "the number of vectors in points")) - - cdef float min_rmsd = -1 - cdef map[unsigned int, unsigned int] results_map = \ + msg = ( + "The number of vectors in ref_points must MATCH" + "the number of vectors in points" + ) + raise ValueError(msg) + + min_rmsd = -1 + results_map = \ freud._environment.minimizeRMSD( - dereference(b.thisptr), - &l_ref_points[0, 0], - &l_points[0, 0], + b._cpp_obj, + ref_points, + points, nRef1, min_rmsd, registration) - return [min_rmsd, np.asarray(l_points), results_map] + return [min_rmsd, np.asarray(points), results_map] def _is_similar_motif(box, ref_points, points, threshold, registration=False): @@ -467,67 +601,58 @@ def _is_similar_motif(box, ref_points, points, threshold, registration=False): correspond to each other. Empty if they do not correspond to each other. """ # noqa: E501 - cdef freud.box.Box b = freud.util._convert_box(box) + b = freud.util._convert_box(box) ref_points = freud.util._convert_array(ref_points, shape=(None, 3)) points = freud.util._convert_array(points, shape=(None, 3)) - cdef const float[:, ::1] l_ref_points = ref_points - cdef const float[:, ::1] l_points = points - cdef unsigned int nRef1 = l_ref_points.shape[0] - cdef unsigned int nRef2 = l_points.shape[0] - cdef float threshold_sq = threshold*threshold + nRef1 = ref_points.shape[0] + nRef2 = points.shape[0] + threshold_sq = threshold*threshold if nRef1 != nRef2: - raise ValueError( - ("The number of vectors in ref_points must match" - "the number of vectors in points")) - - cdef map[unsigned int, unsigned int] vec_map = \ - freud._environment.isSimilar( - dereference(b.thisptr), &l_ref_points[0, 0], - &l_points[0, 0], nRef1, threshold_sq, + msg = ( + "The number of vectors in ref_points must match" + "the number of vectors in points" + ) + raise ValueError(msg) + + vec_map = freud._environment.isSimilar( + b._cpp_obj, + ref_points, + points, + nRef1, + threshold_sq, registration) - return [np.asarray(l_points), vec_map] + return [np.asarray(points), vec_map] -cdef class _MatchEnv(_PairCompute): +class _MatchEnv(_PairCompute): r"""Parent for environment matching methods. """ - cdef freud._environment.MatchEnv * matchptr - def __cinit__(self, *args, **kwargs): + def __init__(self, *args, **kwargs): # Abstract class - pass + self._cpp_obj = freud._environment.MatchEnv() @_Compute._computed_property def point_environments(self): """:math:`\\left(N_{points}, N_{neighbors}, 3\\right)` list[:class:`numpy.ndarray`]: All environments for all points.""" - envs = self.matchptr.getPointEnvironments() + envs = self._cpp_obj.getPointEnvironments() return [np.asarray([[p.x, p.y, p.z] for p in env]) for env in envs] def __repr__(self): - return ("freud.environment.{cls}()").format( - cls=type(self).__name__) + return (f"freud.environment.{type(self).__name__}()") -cdef class EnvironmentCluster(_MatchEnv): +class EnvironmentCluster(_MatchEnv): r"""Clusters particles according to whether their local environments match or not, using various shape matching metrics defined in :cite:`Teich2019`. """ - cdef freud._environment.EnvironmentCluster * thisptr - - def __cinit__(self): - self.thisptr = self.matchptr = \ - new freud._environment.EnvironmentCluster() - def __init__(self): - pass - - def __dealloc__(self): - del self.thisptr + self._cpp_obj = freud._environment.EnvironmentCluster() def compute(self, system, threshold, cluster_neighbors=None, env_neighbors=None, registration=False): @@ -638,13 +763,6 @@ def compute(self, system, threshold, cluster_neighbors=None, option incurs a significant performance penalty. (Default value = :code:`False`) """ # noqa: E501 - cdef: - freud.locality.NeighborQuery nq - freud.locality.NeighborList nlist, env_nlist - freud.locality._QueryArgs qargs, env_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=cluster_neighbors) @@ -652,9 +770,12 @@ def compute(self, system, threshold, cluster_neighbors=None, env_neighbors = cluster_neighbors env_nlist, env_qargs = self._resolve_neighbors(env_neighbors) - self.thisptr.compute( - nq.get_ptr(), nlist.get_ptr(), dereference(qargs.thisptr), - env_nlist.get_ptr(), dereference(env_qargs.thisptr), threshold, + self._cpp_obj.compute( + nq._cpp_obj, + qargs._cpp_obj, + env_nlist._cpp_obj, + env_qargs._cpp_obj, + threshold, registration) return self @@ -662,20 +783,18 @@ def compute(self, system, threshold, cluster_neighbors=None, def cluster_idx(self): """:math:`\\left(N_{particles}\\right)` :class:`numpy.ndarray`: The per-particle index indicating cluster membership.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getClusters(), - freud.util.arr_type_t.UNSIGNED_INT) + return self._cpp_obj.getClusters().toNumpyArray() @_Compute._computed_property def num_clusters(self): """unsigned int: The number of clusters.""" - return self.thisptr.getNumClusters() + return self._cpp_obj.getNumClusters() @_Compute._computed_property def cluster_environments(self): """:math:`\\left(N_{clusters}, N_{neighbors}, 3\\right)` list[:class:`numpy.ndarray`]: The environments for all clusters.""" - envs = self.thisptr.getClusterEnvironments() + envs = self._cpp_obj.getClusterEnvironments() return [np.asarray([[p.x, p.y, p.z] for p in env]) for env in envs] @@ -707,7 +826,7 @@ def _repr_png_(self): return None -cdef class EnvironmentMotifMatch(_MatchEnv): +class EnvironmentMotifMatch(_MatchEnv): r"""Find matches between local arrangements of a set of points and a provided motif, as done in :cite:`Teich2019`. @@ -716,16 +835,10 @@ def _repr_png_(self): different number of neighbors than the motif will always fail to match the motif. See :class:`freud.environment.EnvironmentCluster.compute` for the matching criterion. - """ # noqa: E501 - - cdef freud._environment.EnvironmentMotifMatch * thisptr - - def __cinit__(self): - self.thisptr = self.matchptr = \ - new freud._environment.EnvironmentMotifMatch() + """ def __init__(self): - pass + self._cpp_obj = freud._environment.EnvironmentMotifMatch() def compute(self, system, motif, threshold, env_neighbors=None, registration=False): @@ -763,13 +876,6 @@ def compute(self, system, motif, threshold, env_neighbors=None, it minimizes the RMSD between the two sets (Default value = False). """ - 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=env_neighbors) @@ -778,29 +884,29 @@ def compute(self, system, motif, threshold, env_neighbors=None, warnings.warn( "Attempting to match a motif containing the zero " "vector is likely to result in zero matches.", - RuntimeWarning + RuntimeWarning, + stacklevel=2 ) - - cdef const float[:, ::1] l_motif = motif - cdef unsigned int nRef = l_motif.shape[0] - - self.thisptr.compute( - nq.get_ptr(), nlist.get_ptr(), dereference(qargs.thisptr), - - &l_motif[0, 0], nRef, - threshold, registration) + nRef = motif.shape[0] + + self._cpp_obj.compute( + nq._cpp_obj, + nlist._cpp_obj, + qargs._cpp_obj, + motif, + nRef, + threshold, + registration) return self @_Compute._computed_property def matches(self): """(:math:`N_{points}`) :class:`numpy.ndarray`: A boolean array indicating whether each point matches the motif.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getMatches(), - freud.util.arr_type_t.BOOL) + return self._cpp_obj.getMatches().toNumpyArray() -cdef class _EnvironmentRMSDMinimizer(_MatchEnv): +class _EnvironmentRMSDMinimizer(_MatchEnv): r"""Find linear transformations that map the environments of points onto a motif. @@ -814,14 +920,8 @@ def matches(self): enforced (but we could add a warning to the compute...). """ - cdef freud._environment.EnvironmentRMSDMinimizer * thisptr - - def __cinit__(self): - self.thisptr = self.matchptr = \ - new freud._environment.EnvironmentRMSDMinimizer() - def __init__(self): - pass + self._cpp_obj = freud._environment.EnvironmentRMSDMinimizer() def compute(self, system, motif, neighbors=None, registration=False): @@ -851,243 +951,40 @@ def compute(self, system, motif, neighbors=None, Vector of minimal RMSD values, one value per particle. """ - 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) motif = freud.util._convert_array(motif, shape=(None, 3)) - cdef const float[:, ::1] l_motif = motif - cdef unsigned int nRef = l_motif.shape[0] - - self.thisptr.compute( - nq.get_ptr(), nlist.get_ptr(), dereference(qargs.thisptr), - - &l_motif[0, 0], nRef, + nRef = motif.shape[0] + + self._cpp_obj.compute( + nq._cpp_obj, + nlist._cpp_obj, + qargs._cpp_obj, + motif, + nRef, registration) - return self @_Compute._computed_property def rmsds(self): """:math:`(N_p, )` :class:`numpy.ndarray`: A boolean array of the RMSDs found for each point's environment.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getRMSDs(), - freud.util.arr_type_t.FLOAT) - - -cdef class AngularSeparationNeighbor(_PairCompute): - r"""Calculates the minimum angles of separation between orientations and - query orientations.""" - cdef freud._environment.AngularSeparationNeighbor * thisptr - - def __cinit__(self): - self.thisptr = new freud._environment.AngularSeparationNeighbor() - - def __init__(self): - pass - - def __dealloc__(self): - del self.thisptr - - def compute(self, system, orientations, query_points=None, - query_orientations=None, - equiv_orientations=np.array([[1, 0, 0, 0]]), - neighbors=None): - r"""Calculates the minimum angles of separation between :code:`orientations` - and :code:`query_orientations`, checking for underlying symmetry as encoded - in :code:`equiv_orientations`. The result is stored in the :code:`neighbor_angles` - class attribute. - - Args: - system: - Any object that is a valid argument to - :class:`freud.locality.NeighborQuery.from_system`. - orientations ((:math:`N_{points}`, 4) :class:`numpy.ndarray`): - Orientations associated with system points that are used to - calculate bonds. - query_points ((:math:`N_{query\_points}`, 3) :class:`numpy.ndarray`, optional): - Query points used to calculate the correlation function. Uses - the system's points if :code:`None` (Default - value = :code:`None`). - query_orientations ((:math:`N_{query\_points}`, 4) :class:`numpy.ndarray`, optional): - Query orientations used to calculate bonds. Uses - :code:`orientations` if :code:`None`. (Default - value = :code:`None`). - equiv_orientations ((:math:`N_{equiv}`, 4) :class:`numpy.ndarray`, optional): - The set of all equivalent quaternions that map the particle - to itself (the elements of its rotational symmetry group). - Important: :code:`equiv_orientations` must include both - :math:`q` and :math:`-q`, for all included quaternions. Note - that this calculation assumes that all points in the system - share the same set of equivalent orientations. - (Default value = :code:`[[1, 0, 0, 0]]`) - neighbors (:class:`freud.locality.NeighborList` or dict, optional): - Either a :class:`NeighborList ` of - neighbor pairs to use in the calculation, or a dictionary of - `query arguments - `_ - (Default value: None). - """ # noqa: E501 - 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, query_points, neighbors) - - orientations = freud.util._convert_array( - orientations, shape=(nq.points.shape[0], 4)) - if query_orientations is None: - query_orientations = orientations - else: - query_orientations = freud.util._convert_array( - query_orientations, shape=(query_points.shape[0], 4)) - - equiv_orientations = freud.util._convert_array( - equiv_orientations, shape=(None, 4)) - - cdef const float[:, ::1] l_orientations = orientations - cdef const float[:, ::1] l_query_orientations = query_orientations - cdef const float[:, ::1] l_equiv_orientations = equiv_orientations - - cdef unsigned int n_equiv_orientations = l_equiv_orientations.shape[0] - - self.thisptr.compute( - nq.get_ptr(), - &l_orientations[0, 0], - &l_query_points[0, 0], - &l_query_orientations[0, 0], - num_query_points, - &l_equiv_orientations[0, 0], - n_equiv_orientations, - nlist.get_ptr(), - dereference(qargs.thisptr)) - return self - - @_Compute._computed_property - def angles(self): - """:math:`\\left(N_{bonds}\\right)` :class:`numpy.ndarray`: The - neighbor angles in radians. The angles are stored in the order of the - neighborlist object.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getAngles(), - freud.util.arr_type_t.FLOAT) - - def __repr__(self): - return "freud.environment.{cls}()".format( - cls=type(self).__name__) - - @_Compute._computed_property - def nlist(self): - """:class:`freud.locality.NeighborList`: The neighbor list from the - last compute.""" - nlist = freud.locality._nlist_from_cnlist(self.thisptr.getNList()) - nlist._compute = self - return nlist - - -cdef class AngularSeparationGlobal(_Compute): - r"""Calculates the minimum angles of separation between orientations and - global orientations.""" - cdef freud._environment.AngularSeparationGlobal * thisptr - - def __cinit__(self): - self.thisptr = new freud._environment.AngularSeparationGlobal() - - def __init__(self): - pass - - def __dealloc__(self): - del self.thisptr - - def compute(self, global_orientations, orientations, - equiv_orientations=np.array([[1, 0, 0, 0]])): - r"""Calculates the minimum angles of separation between - :code:`global_orientations` and :code:`orientations`, checking for - underlying symmetry as encoded in :code:`equiv_orientations`. The - result is stored in the :code:`global_angles` class attribute. - - Args: - global_orientations ((:math:`N_{global}`, 4) :class:`numpy.ndarray`): - Set of global orientations to calculate the order - parameter. - orientations ((:math:`N_{particles}`, 4) :class:`numpy.ndarray`): - Orientations to calculate the order parameter. - equiv_orientations ((:math:`N_{equiv}`, 4) :class:`numpy.ndarray`, optional): - The set of all equivalent quaternions that map the particle - to itself (the elements of its rotational symmetry group). - Important: :code:`equiv_orientations` must include both - :math:`q` and :math:`-q`, for all included quaternions. Note - that this calculation assumes that all points in the system - share the same set of equivalent orientations. - (Default value = :code:`[[1, 0, 0, 0]]`) - """ # noqa: E501 - global_orientations = freud.util._convert_array( - global_orientations, shape=(None, 4)) - orientations = freud.util._convert_array( - orientations, shape=(None, 4)) - equiv_orientations = freud.util._convert_array( - equiv_orientations, shape=(None, 4)) + return self._cpp_obj.getRMSDs().toNumpyArray() - cdef const float[:, ::1] l_global_orientations = global_orientations - cdef const float[:, ::1] l_orientations = orientations - cdef const float[:, ::1] l_equiv_orientations = equiv_orientations - - cdef unsigned int n_global = l_global_orientations.shape[0] - cdef unsigned int n_points = l_orientations.shape[0] - cdef unsigned int n_equiv_orientations = l_equiv_orientations.shape[0] - - self.thisptr.compute( - &l_global_orientations[0, 0], - n_global, - &l_orientations[0, 0], - n_points, - &l_equiv_orientations[0, 0], - n_equiv_orientations) - return self - @_Compute._computed_property - def angles(self): - """:math:`\\left(N_{orientations}, N_{global\\_orientations}\\right)` :class:`numpy.ndarray`: - The global angles in radians.""" # noqa: E501 - return freud.util.make_managed_numpy_array( - &self.thisptr.getAngles(), - freud.util.arr_type_t.FLOAT) +class LocalBondProjection(_PairCompute): + r"""Calculates the maximal projection of nearest neighbor bonds for each + particle onto some set of reference vectors, defined in the particles' + local reference frame. + """ - def __repr__(self): - return "freud.environment.{cls}()".format( - cls=type(self).__name__) + def __init__(self): + self._cpp_obj = freud._environment.LocalBondProjection() - -cdef class LocalBondProjection(_PairCompute): - r"""Calculates the maximal projection of nearest neighbor bonds for each - particle onto some set of reference vectors, defined in the particles' - local reference frame. - """ - cdef freud._environment.LocalBondProjection * thisptr - - def __cinit__(self): - self.thisptr = new freud._environment.LocalBondProjection() - - def __init__(self): - pass - - def __dealloc__(self): - del self.thisptr - - def compute(self, system, orientations, proj_vecs, - query_points=None, equiv_orientations=np.array([[1, 0, 0, 0]]), - neighbors=None): + def compute(self, system, orientations, proj_vecs, + query_points=None, equiv_orientations=np.array([[1, 0, 0, 0]]), + neighbors=None): r"""Calculates the maximal projections of nearest neighbor bonds (between :code:`points` and :code:`query_points`) onto the set of reference vectors :code:`proj_vecs`, defined in the local reference @@ -1126,14 +1023,7 @@ def compute(self, system, orientations, proj_vecs, `_ (Default value: None). """ # noqa: E501 - 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 = \ + nq, nlist, qargs, query_points, num_query_points = \ self._preprocess_arguments(system, query_points, neighbors) orientations = freud.util._convert_array( @@ -1143,48 +1033,40 @@ def compute(self, system, orientations, proj_vecs, equiv_orientations, shape=(None, 4)) proj_vecs = freud.util._convert_array(proj_vecs, shape=(None, 3)) - cdef const float[:, ::1] l_orientations = orientations - cdef const float[:, ::1] l_equiv_orientations = equiv_orientations - cdef const float[:, ::1] l_proj_vecs = proj_vecs - - cdef unsigned int n_equiv = l_equiv_orientations.shape[0] - cdef unsigned int n_proj = l_proj_vecs.shape[0] - - self.thisptr.compute( - nq.get_ptr(), - &l_orientations[0, 0], - &l_query_points[0, 0], num_query_points, - &l_proj_vecs[0, 0], n_proj, - &l_equiv_orientations[0, 0], n_equiv, - nlist.get_ptr(), dereference(qargs.thisptr)) + self._cpp_obj.compute( + nq._cpp_obj, + orientations, + query_points, + proj_vecs, + equiv_orientations, + nlist._cpp_obj, + qargs._cpp_obj, + ) return self - @_Compute._computed_property - def nlist(self): + @_Compute._computed_property + def nlist(self): """:class:`freud.locality.NeighborList`: The neighbor list from the last compute.""" - nlist = freud.locality._nlist_from_cnlist(self.thisptr.getNList()) + nlist = freud.locality._nlist_from_cnlist(self._cpp_obj.getNList()) nlist._compute = self return nlist - @_Compute._computed_property - def projections(self): + @_Compute._computed_property + def projections(self): """:math:`\\left(N_{bonds}, N_{projection\\_vecs} \\right)` :class:`numpy.ndarray`: The projection of each bond between query particles and their neighbors onto each of the projection vectors.""" # noqa: E501 - return freud.util.make_managed_numpy_array( - &self.thisptr.getProjections(), - freud.util.arr_type_t.FLOAT) + return self._cpp_obj.getProjections().toNumpyArray() - @_Compute._computed_property - def normed_projections(self): + @_Compute._computed_property + def normed_projections(self): """:math:`\\left(N_{bonds}, N_{projection\\_vecs} \\right)` :class:`numpy.ndarray`: The projection of each bond between query particles and their neighbors onto each of the projection vectors, normalized by the length of the bond.""" # noqa: E501 - return freud.util.make_managed_numpy_array( - &self.thisptr.getNormedProjections(), - freud.util.arr_type_t.FLOAT) + return self._cpp_obj.getNormedProjections().toNumpyArray() + + def __repr__(self): + return (f"freud.environment.{type(self).__name__}()") - def __repr__(self): - return ("freud.environment.{cls}()").format(cls=type(self).__name__) diff --git a/freud/environment/AngularSeparation.cc b/freud/environment/AngularSeparation.cc index 6646491be..1d36fa7fe 100644 --- a/freud/environment/AngularSeparation.cc +++ b/freud/environment/AngularSeparation.cc @@ -49,32 +49,33 @@ float computeMinSeparationAngle(const quat& ref_q, const quat& q, return min_angle; } -void AngularSeparationNeighbor::compute(const locality::NeighborQuery* nq, const quat* orientations, +void AngularSeparationNeighbor::compute(const std::shared_ptr nq, const quat* orientations, const vec3* query_points, const quat* query_orientations, unsigned int n_query_points, const quat* equiv_orientations, unsigned int n_equiv_orientations, - const freud::locality::NeighborList* nlist, locality::QueryArgs qargs) + const std::shared_ptr nlist, const locality::QueryArgs& qargs) { // This function requires a NeighborList object, so we always make one and store it locally. + m_nlist = locality::makeDefaultNlist(nq, nlist, query_points, n_query_points, qargs); - const size_t tot_num_neigh = m_nlist.getNumBonds(); - m_angles.prepare(tot_num_neigh); + const size_t tot_num_neigh = m_nlist->getNumBonds(); + m_angles = std::make_shared>(std::vector {tot_num_neigh}); util::forLoopWrapper(0, nq->getNPoints(), [&](size_t begin, size_t end) { - size_t bond(m_nlist.find_first_index(begin)); + size_t bond(m_nlist->find_first_index(begin)); for (size_t i = begin; i < end; ++i) { quat q = orientations[i]; - for (; bond < tot_num_neigh && m_nlist.getNeighbors()(bond, 0) == i; ++bond) + for (; bond < tot_num_neigh && (*m_nlist->getNeighbors())(bond, 0) == i; ++bond) { - const size_t j(m_nlist.getNeighbors()(bond, 1)); + const size_t j((*m_nlist->getNeighbors())(bond, 1)); quat query_q = query_orientations[j]; float theta = computeMinSeparationAngle(q, query_q, equiv_orientations, n_equiv_orientations); - m_angles[bond] = theta; + (*m_angles)[bond] = theta; } } }); @@ -85,7 +86,7 @@ void AngularSeparationGlobal::compute(const quat* global_orientations, un const quat* equiv_orientations, unsigned int n_equiv_orientations) { - m_angles.prepare({n_points, n_global}); + m_angles = std::make_shared>(std::vector {n_points, n_global}); util::forLoopWrapper(0, n_points, [&](size_t begin, size_t end) { for (size_t i = begin; i < end; ++i) @@ -96,7 +97,7 @@ void AngularSeparationGlobal::compute(const quat* global_orientations, un quat global_q = global_orientations[j]; float theta = computeMinSeparationAngle(q, global_q, equiv_orientations, n_equiv_orientations); - m_angles(i, j) = theta; + (*m_angles)(i, j) = theta; } } }); diff --git a/freud/environment/AngularSeparation.h b/freud/environment/AngularSeparation.h index a45168959..ff921debd 100644 --- a/freud/environment/AngularSeparation.h +++ b/freud/environment/AngularSeparation.h @@ -36,13 +36,13 @@ class AngularSeparationGlobal const quat* equiv_orientations, unsigned int n_equiv_orientations); //! Returns the last computed global angle array - const util::ManagedArray& getAngles() const + std::shared_ptr> getAngles() const { return m_angles; } private: - util::ManagedArray m_angles; //!< Global angle array computed + std::shared_ptr> m_angles; //!< Global angle array computed }; //! Compute the difference in orientation between pairs of points. @@ -60,27 +60,27 @@ class AngularSeparationNeighbor ~AngularSeparationNeighbor() = default; //! Compute the angular separation between neighbors - void compute(const locality::NeighborQuery* nq, const quat* orientations, + void compute(const std::shared_ptr nq, const quat* orientations, const vec3* query_points, const quat* query_orientations, unsigned int n_query_points, const quat* equiv_orientations, - unsigned int n_equiv_orientations, const freud::locality::NeighborList* nlist, - locality::QueryArgs qargs); + unsigned int n_equiv_orientations, const std::shared_ptr nlist, + const locality::QueryArgs& qargs); //! Returns the last computed neighbor angle array - const util::ManagedArray& getAngles() const + std::shared_ptr> getAngles() const { return m_angles; } //! Return a pointer to the NeighborList used in the last call to compute. - locality::NeighborList* getNList() + std::shared_ptr getNList() { - return &m_nlist; + return m_nlist; } private: - util::ManagedArray m_angles; //!< neighbor angle array computed - locality::NeighborList m_nlist; //!< The NeighborList used in the last call to compute. + std::shared_ptr> m_angles; //!< neighbor angle array computed + std::shared_ptr m_nlist; //!< The NeighborList used in the last call to compute. }; }; }; // end namespace freud::environment diff --git a/freud/environment/BondOrder.cc b/freud/environment/BondOrder.cc index 376182c3a..3c838ff66 100644 --- a/freud/environment/BondOrder.cc +++ b/freud/environment/BondOrder.cc @@ -33,8 +33,8 @@ BondOrder::BondOrder(unsigned int n_bins_theta, unsigned int n_bins_phi, BondOrd /* 0 < \theta < 2PI; 0 < \phi < PI */ - float dt = constants::TWO_PI / float(n_bins_theta); - float dp = M_PI / float(n_bins_phi); + const float dt = constants::TWO_PI / float(n_bins_theta); + const float dp = M_PI / float(n_bins_phi); // this shouldn't be able to happen, but it's always better to check if (dt > constants::TWO_PI) { @@ -46,41 +46,52 @@ BondOrder::BondOrder(unsigned int n_bins_theta, unsigned int n_bins_phi, BondOrd } // precompute the surface area array - m_sa_array.prepare({n_bins_theta, n_bins_phi}); + // m_sa_array.prepare({n_bins_theta, n_bins_phi}); + m_sa_array = std::make_shared>(std::vector {n_bins_theta, n_bins_phi}); for (unsigned int i = 0; i < n_bins_theta; i++) { for (unsigned int j = 0; j < n_bins_phi; j++) { - float phi = (float) j * dp; - float sa = dt * (std::cos(phi) - std::cos(phi + dp)); - m_sa_array(i, j) = sa; + const float phi = (float) j * dp; + const float sa = dt * (std::cos(phi) - std::cos(phi + dp)); + (*m_sa_array)(i, j) = sa; } } const auto axes = util::Axes {std::make_shared(n_bins_theta, 0, constants::TWO_PI), std::make_shared(n_bins_phi, 0, M_PI)}; m_histogram = BondHistogram(axes); + m_bo_array = std::make_shared>(std::vector {m_histogram.shape()}); m_local_histograms = BondHistogram::ThreadLocalHistogram(m_histogram); } -void BondOrder::reduce() +void BondOrder::reset() { - m_histogram.prepare(m_histogram.shape()); - m_bo_array.prepare(m_histogram.shape()); + BondHistogramCompute::reset(); + m_bo_array = std::make_shared>(std::vector {m_histogram.shape()}); +} +void BondOrder::reduce() +{ m_histogram.reduceOverThreadsPerBin(m_local_histograms, [&](size_t i) { - m_bo_array[i] = m_histogram[i] / m_sa_array[i] / static_cast(m_frame_counter); + (*m_bo_array)[i] = m_histogram[i] / (*m_sa_array)[i] / static_cast(m_frame_counter); }); } -const util::ManagedArray& BondOrder::getBondOrder() +std::vector> BondOrder::getBinCenters() +{ + return m_histogram.getBinCenters(); +} + +const std::shared_ptr> BondOrder::getBondOrder() { return reduceAndReturn(m_bo_array); } -void BondOrder::accumulate(const locality::NeighborQuery* neighbor_query, quat* orientations, - vec3* query_points, quat* query_orientations, - unsigned int n_query_points, const freud::locality::NeighborList* nlist, +void BondOrder::accumulate(const std::shared_ptr& neighbor_query, + quat* orientations, vec3* query_points, + quat* query_orientations, unsigned int n_query_points, + const std::shared_ptr& nlist, freud::locality::QueryArgs qargs) { accumulateGeneral(neighbor_query, query_points, n_query_points, nlist, qargs, diff --git a/freud/environment/BondOrder.h b/freud/environment/BondOrder.h index 45ca18644..6db775a9a 100644 --- a/freud/environment/BondOrder.h +++ b/freud/environment/BondOrder.h @@ -40,14 +40,17 @@ class BondOrder : public locality::BondHistogramCompute ~BondOrder() override = default; //! Accumulate the bond order - void accumulate(const locality::NeighborQuery* neighbor_query, quat* orientations, + void accumulate(const std::shared_ptr& neighbor_query, quat* orientations, vec3* query_points, quat* query_orientations, unsigned int n_query_points, - const freud::locality::NeighborList* nlist, freud::locality::QueryArgs qargs); + const std::shared_ptr& nlist, + freud::locality::QueryArgs qargs); void reduce() override; + void reset() override; + std::vector> getBinCenters(); - //! Get a reference to the last computed bond order - const util::ManagedArray& getBondOrder(); + //! Get a shared_ptr to the last computed bond order + const std::shared_ptr> getBondOrder(); BondOrderMode getMode() const { @@ -55,9 +58,9 @@ class BondOrder : public locality::BondHistogramCompute } private: - util::ManagedArray m_bo_array; //!< bond order array computed - util::ManagedArray m_sa_array; //!< surface area array computed - BondOrderMode m_mode; //!< The mode to calculate with. + std::shared_ptr> m_bo_array; //!< bond order array computed + std::shared_ptr> m_sa_array; //!< surface area array computed + BondOrderMode m_mode; //!< The mode to calculate with. }; }; }; // end namespace freud::environment diff --git a/freud/environment/LocalBondProjection.cc b/freud/environment/LocalBondProjection.cc index 33d8e3f12..b6bec3871 100644 --- a/freud/environment/LocalBondProjection.cc +++ b/freud/environment/LocalBondProjection.cc @@ -46,45 +46,46 @@ float computeMaxProjection(const vec3& proj_vec, const vec3& local return max_proj; } -void LocalBondProjection::compute(const locality::NeighborQuery* nq, const quat* orientations, +void LocalBondProjection::compute(const std::shared_ptr nq, const quat* orientations, const vec3* query_points, unsigned int n_query_points, const vec3* proj_vecs, unsigned int n_proj, const quat* equiv_orientations, unsigned int n_equiv_orientations, - const freud::locality::NeighborList* nlist, locality::QueryArgs qargs) + const std::shared_ptr nlist, const locality::QueryArgs& qargs) { // This function requires a NeighborList object, so we always make one and store it locally. m_nlist = locality::makeDefaultNlist(nq, nlist, query_points, n_query_points, qargs); // Get the maximum total number of bonds in the neighbor list - const unsigned int tot_num_neigh = m_nlist.getNumBonds(); + const unsigned int tot_num_neigh = m_nlist->getNumBonds(); + const auto& neighbors = m_nlist->getNeighbors(); - m_local_bond_proj.prepare({tot_num_neigh, n_proj}); - m_local_bond_proj_norm.prepare({tot_num_neigh, n_proj}); + m_local_bond_proj = std::make_shared>(std::vector {tot_num_neigh, n_proj}); + m_local_bond_proj_norm = std::make_shared>(std::vector {tot_num_neigh, n_proj}); // compute the order parameter util::forLoopWrapper(0, n_query_points, [&](size_t begin, size_t end) { - size_t bond(m_nlist.find_first_index(begin)); + size_t bond(m_nlist->find_first_index(begin)); for (size_t i = begin; i < end; ++i) { - for (; bond < tot_num_neigh && m_nlist.getNeighbors()(bond, 0) == i; ++bond) + for (; bond < tot_num_neigh && (* neighbors)(bond, 0) == i; ++bond) { - const size_t j(m_nlist.getNeighbors()(bond, 1)); + const size_t j( (* neighbors)(bond, 1)); // compute bond vector between the two particles - vec3 local_bond(m_nlist.getVectors()[bond]); + vec3 local_bond((* m_nlist->getVectors())(bond)); // rotate bond vector into the local frame of particle p local_bond = rotate(conj(orientations[j]), local_bond); // store the length of this local bond - float local_bond_len = m_nlist.getDistances()[bond]; + float local_bond_len = (* m_nlist->getDistances())(bond); for (unsigned int k = 0; k < n_proj; k++) { vec3 proj_vec = proj_vecs[k]; float max_proj = computeMaxProjection(proj_vec, local_bond, equiv_orientations, n_equiv_orientations); - m_local_bond_proj(bond, k) = max_proj; - m_local_bond_proj_norm(bond, k) = max_proj / local_bond_len; + (*m_local_bond_proj)(bond, k) = max_proj; + (*m_local_bond_proj_norm)(bond, k) = max_proj / local_bond_len; } } } diff --git a/freud/environment/LocalBondProjection.h b/freud/environment/LocalBondProjection.h index a016e1b10..01e4ab198 100644 --- a/freud/environment/LocalBondProjection.h +++ b/freud/environment/LocalBondProjection.h @@ -32,35 +32,35 @@ class LocalBondProjection ~LocalBondProjection() = default; //! Compute the maximal local bond projection - void compute(const locality::NeighborQuery* nq, const quat* orientations, + void compute(const std::shared_ptr nq, const quat* orientations, const vec3* query_points, unsigned int n_query_points, const vec3* proj_vecs, unsigned int n_proj, const quat* equiv_orientations, - unsigned int n_equiv_orientations, const freud::locality::NeighborList* nlist, - locality::QueryArgs qargs); + unsigned int n_equiv_orientations, const std::shared_ptr nlist, + const locality::QueryArgs& qargs); //! Get a reference to the last computed maximal local bond projection array - const util::ManagedArray& getProjections() const + std::shared_ptr> getProjections() const { return m_local_bond_proj; } //! Get a reference to the last computed normalized maximal local bond projection array - const util::ManagedArray& getNormedProjections() const + std::shared_ptr> getNormedProjections() const { return m_local_bond_proj_norm; } //! Return a pointer to the NeighborList used in the last call to compute. - locality::NeighborList* getNList() + std::shared_ptr getNList() { - return &m_nlist; + return m_nlist; } private: - locality::NeighborList m_nlist; //!< The NeighborList used in the last call to compute. + std::shared_ptr m_nlist; //!< The NeighborList used in the last call to compute. - util::ManagedArray m_local_bond_proj; //!< Local bond projection array computed - util::ManagedArray m_local_bond_proj_norm; //!< Normalized local bond projection array computed + std::shared_ptr> m_local_bond_proj; //!< Local bond projection array computed + std::shared_ptr> m_local_bond_proj_norm; //!< Normalized local bond projection array computed }; }; }; // end namespace freud::environment diff --git a/freud/environment/LocalDescriptors.cc b/freud/environment/LocalDescriptors.cc index 1ddfee993..df9d11010 100644 --- a/freud/environment/LocalDescriptors.cc +++ b/freud/environment/LocalDescriptors.cc @@ -18,9 +18,12 @@ LocalDescriptors::LocalDescriptors(unsigned int l_max, bool negative_m, : m_l_max(l_max), m_negative_m(negative_m), m_nSphs(0), m_orientation(orientation) {} -void LocalDescriptors::compute(const locality::NeighborQuery* nq, const vec3* query_points, - unsigned int n_query_points, const quat* orientations, - const freud::locality::NeighborList* nlist, locality::QueryArgs qargs, +void LocalDescriptors::compute(const std::shared_ptr nq, + const vec3* query_points, + unsigned int n_query_points, + const quat* orientations, + const std::shared_ptr nlist, + const locality::QueryArgs& qargs, unsigned int max_num_neighbors) { // This function requires a NeighborList object, so we always make one and store it locally. @@ -30,14 +33,15 @@ void LocalDescriptors::compute(const locality::NeighborQuery* nq, const vec3::max(); } - m_sphArray.prepare({m_nlist.getNumBonds(), getSphWidth()}); + m_sphArray = std::make_shared>>( + std::vector{m_nlist->getNumBonds(), getSphWidth()}); util::forLoopWrapper(0, nq->getNPoints(), [&](size_t begin, size_t end) { fsph::PointSPHEvaluator sph_eval(m_l_max); for (size_t i = begin; i < end; ++i) { - size_t bond(m_nlist.find_first_index(i)); + size_t bond(m_nlist->find_first_index(i)); unsigned int neighbor_count(0); vec3 rotation_0; @@ -47,12 +51,11 @@ void LocalDescriptors::compute(const locality::NeighborQuery* nq, const vec3 inertiaTensor = util::ManagedArray({3, 3}); - - for (size_t bond_copy(bond); bond_copy < m_nlist.getNumBonds() - && m_nlist.getNeighbors()(bond_copy, 0) == i && neighbor_count < max_num_neighbors; + for (size_t bond_copy(bond); bond_copy < m_nlist->getNumBonds() + && (*m_nlist->getNeighbors())(bond_copy, 0) == i && neighbor_count < max_num_neighbors; ++bond_copy, ++neighbor_count) { - const vec3 r_ij(m_nlist.getVectors()[bond_copy]); + const vec3 r_ij((* m_nlist->getVectors())(bond_copy)); const float r_sq(dot(r_ij, r_ij)); for (size_t ii(0); ii < 3; ++ii) @@ -99,13 +102,13 @@ void LocalDescriptors::compute(const locality::NeighborQuery* nq, const vec3getNumBonds() && (*m_nlist->getNeighbors())(bond, 0) == i && neighbor_count < max_num_neighbors; ++bond, ++neighbor_count) { const unsigned int sphCount(bond * getSphWidth()); - const vec3 r_ij(m_nlist.getVectors()[bond]); - const float magR(m_nlist.getDistances()[bond]); + const vec3 r_ij((* m_nlist->getVectors())(bond)); + const float magR((* m_nlist->getDistances())(bond)); const vec3 bond_ij(dot(rotation_0, r_ij), dot(rotation_1, r_ij), dot(rotation_2, r_ij)); @@ -124,14 +127,14 @@ void LocalDescriptors::compute(const locality::NeighborQuery* nq, const vec3data() + sphCount); } } }); // save the last computed number of particles - m_nSphs = m_nlist.getNumBonds(); + m_nSphs = m_nlist->getNumBonds(); } }; }; // end namespace freud::environment diff --git a/freud/environment/LocalDescriptors.h b/freud/environment/LocalDescriptors.h index 153a6425d..5bf19b050 100644 --- a/freud/environment/LocalDescriptors.h +++ b/freud/environment/LocalDescriptors.h @@ -52,13 +52,16 @@ class LocalDescriptors //! Compute the local neighborhood descriptors given some //! positions and the number of particles - void compute(const locality::NeighborQuery* nq, const vec3* query_points, - unsigned int n_query_points, const quat* orientations, - const freud::locality::NeighborList* nlist, locality::QueryArgs qargs, - unsigned int max_num_neighbors = 0); + void compute(const std::shared_ptr nq, + const vec3* query_points, + unsigned int n_query_points, + const quat* orientations, + const std::shared_ptr nlist, + const locality::QueryArgs& qargs, + unsigned int max_num_neighbor); //! Get a reference to the last computed spherical harmonic array - const util::ManagedArray>& getSph() const + std::shared_ptr>> getSph() const { return m_sphArray; } @@ -70,9 +73,9 @@ class LocalDescriptors } //! Return a pointer to the NeighborList used in the last call to compute. - locality::NeighborList* getNList() + std::shared_ptr getNList() { - return &m_nlist; + return m_nlist; } bool getNegativeM() const @@ -89,11 +92,11 @@ class LocalDescriptors unsigned int m_l_max; //!< Maximum spherical harmonic l to calculate bool m_negative_m; //!< true if we should compute Ylm for negative m unsigned int m_nSphs; //!< Last number of bond spherical harmonics computed - locality::NeighborList m_nlist; //!< The NeighborList used in the last call to compute. + std::shared_ptr m_nlist; //!< The NeighborList used in the last call to compute. LocalDescriptorOrientation m_orientation; //!< The orientation mode to compute with. //! Spherical harmonics for each neighbor - util::ManagedArray> m_sphArray; + std::shared_ptr>> m_sphArray; }; }; }; // end namespace freud::environment diff --git a/freud/environment/export-AngularSeparationGlobal.cc b/freud/environment/export-AngularSeparationGlobal.cc new file mode 100644 index 000000000..ab984cc33 --- /dev/null +++ b/freud/environment/export-AngularSeparationGlobal.cc @@ -0,0 +1,49 @@ +// 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 +#include +#include +#include // NOLINT(misc-include-cleaner): used implicitly +#include // NOLINT(misc-include-cleaner): used implicitly + +#include "AngularSeparation.h" + + +namespace nb = nanobind; + +namespace freud { namespace environment { + +template +using nb_array = nanobind::ndarray; + +namespace wrap { +void compute(const std::shared_ptr& angular_separation, + const nb_array>& global_orientations, + const nb_array>& orientations, + const nb_array>& equiv_orientations) + { + unsigned int const n_global = global_orientations.shape(0); + unsigned int const n_points = orientations.shape(0); + unsigned int const n_equiv_orientations = equiv_orientations.shape(0); + auto* global_orientations_data = reinterpret_cast*>(global_orientations.data()); + auto* orientations_data = reinterpret_cast*>(orientations.data()); + auto* equiv_orientations_data = reinterpret_cast*>(equiv_orientations.data()); + angular_separation->compute(global_orientations_data, n_global, orientations_data, n_points, equiv_orientations_data, n_equiv_orientations); + } + +}; + +namespace detail { + +void export_AngularSeparationGlobal(nb::module_& module) +{ + nb::class_(module, "AngularSeparationGlobal") + .def(nb::init<>()) + .def("getAngles", &AngularSeparationGlobal::getAngles) + .def("compute", &wrap::compute, nb::arg("global_orientations"), nb::arg("orientations"), + nb::arg("equiv_orientations")); +} + +}; }; // namespace detail +}; // namespace freud::locality diff --git a/freud/environment/export-AngularSeparationNeighbor.cc b/freud/environment/export-AngularSeparationNeighbor.cc new file mode 100644 index 000000000..1a1697ae8 --- /dev/null +++ b/freud/environment/export-AngularSeparationNeighbor.cc @@ -0,0 +1,59 @@ +// 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 +#include +#include +#include // NOLINT(misc-include-cleaner): used implicitly +#include // NOLINT(misc-include-cleaner): used implicitly +#include + +#include "AngularSeparation.h" + +namespace nb = nanobind; + +namespace freud { namespace environment { + +template +using nb_array = nanobind::ndarray; + +namespace wrap { + +void compute( + std::shared_ptr& angular_separation, + std::shared_ptr nq, + nb_array>& orientations, + nb_array>& query_points, + nb_array>& query_orientations, + nb_array>& equiv_orientations, + std::shared_ptr nlist, + const locality::QueryArgs& qargs +) + { + auto* orientations_data = reinterpret_cast*>(orientations.data()); + auto* query_points_data = reinterpret_cast*>(query_points.data()); + auto* query_orientations_data = reinterpret_cast*>(query_orientations.data()); + auto* equiv_orientations_data = reinterpret_cast*>(equiv_orientations.data()); + unsigned int n_equiv_orientations = equiv_orientations.shape(0); + unsigned int n_query_points = query_orientations.shape(0); + angular_separation->compute(nq, orientations_data, query_points_data, query_orientations_data, n_query_points, equiv_orientations_data, n_equiv_orientations, nlist, qargs); + } +}; + +namespace detail { + +void export_AngularSeparationNeighbor(nb::module_& module) +{ + nb::class_(module, "AngularSeparationNeighbor") + .def(nb::init<>()) + .def("getNList", &AngularSeparationNeighbor::getNList) + .def("getAngles", &AngularSeparationNeighbor::getAngles) + .def("compute", &wrap::compute, nb::arg("nq"), nb::arg("orientations"), + nb::arg("query_points"), nb::arg("query_orientations"), + nb::arg("equiv_orientations"), nb::arg("nlist").none(), + nb::arg("qargs")); +} + +}; // namespace detail + +}; }; // namespace freud::locality diff --git a/freud/environment/export-BondOrder.cc b/freud/environment/export-BondOrder.cc new file mode 100644 index 000000000..b566b19b2 --- /dev/null +++ b/freud/environment/export-BondOrder.cc @@ -0,0 +1,78 @@ +// 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 +#include +#include +#include // NOLINT(misc-include-cleaner): used implicitly +#include // NOLINT(misc-include-cleaner): used implicitly +#include + +#include "BondOrder.h" + +namespace nb = nanobind; + +namespace freud { namespace environment { + +template using nb_array = nb::ndarray; + +namespace wrap { + +void accumulateBondOrder(const std::shared_ptr& self, + const std::shared_ptr& nq, + const nb_array>& orientations, + const nb_array>& query_points, + const nb_array>& query_orientations, + std::shared_ptr nlist, const locality::QueryArgs& qargs) +{ + unsigned int const n_query_points = query_points.shape(0); + // std::cout << n_query_points << std::endl; + + // if (query_points.is_none()){ + // auto* query_points_data = nq->getPoints(); + // } + // else { + // auto* query_points_data= reinterpret_cast*>(query_points.data()); + // } + + auto* orientations_data = reinterpret_cast*>(orientations.data()); + auto* query_points_data = reinterpret_cast*>(query_points.data()); + auto* query_orientations_data = reinterpret_cast*>(query_orientations.data()); + + self->accumulate(nq, orientations_data, query_points_data, query_orientations_data, n_query_points, nlist, + qargs); +} + +}; // namespace wrap + +namespace detail { + +void export_BondOrder(nb::module_& module) +{ + nb::enum_(module, "BondOrderMode") + .value("bod", BondOrderMode::bod) + .value("lbod", BondOrderMode::lbod) + .value("obcd", BondOrderMode::obcd) + .value("oocd", BondOrderMode::oocd) + .export_values(); + + nb::class_(module, "BondOrder") + .def(nb::init()) + .def("getBondOrder", &BondOrder::getBondOrder) + .def("getBinCounts", &BondOrder::getBinCounts) + .def("getBinCenters", &BondOrder::getBinCenters) + .def("getBinEdges", &BondOrder::getBinEdges) + .def("getBox", &BondOrder::getBox) + .def("getAxisSizes", &BondOrder::getAxisSizes) + .def("getMode", &BondOrder::getMode) + .def("accumulate", &wrap::accumulateBondOrder, nanobind::arg("nq").none(), + nanobind::arg("orientations"), nanobind::arg("query_points"), + nanobind::arg("query_orientations"), + // nanobind::arg("n_query_points"), + nanobind::arg("nlist").none(), nanobind::arg("qargs").none()) + .def("reset", &BondOrder::reset); +} + +}; // namespace detail + +}; }; // namespace freud::environment diff --git a/freud/environment/export-LocalBondProjection.cc b/freud/environment/export-LocalBondProjection.cc new file mode 100644 index 000000000..993928a21 --- /dev/null +++ b/freud/environment/export-LocalBondProjection.cc @@ -0,0 +1,61 @@ +// 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 +#include +#include +#include // NOLINT(misc-include-cleaner): used implicitly +#include // NOLINT(misc-include-cleaner): used implicitly + +#include "LocalBondProjection.h" + + +namespace nb = nanobind; + +namespace freud { namespace environment { + +template +using nb_array = nanobind::ndarray; + +namespace wrap { + +void compute( + std::shared_ptr& local_bond_projection, + std::shared_ptr nq, + nb_array>& orientations, + nb_array>& query_points, + nb_array>& projected_vectors, + nb_array>& equiv_orientations, + std::shared_ptr nlist, + const locality::QueryArgs& qargs +) + { + auto* orientations_data = reinterpret_cast*>(orientations.data()); + auto* query_points_data = reinterpret_cast*>(query_points.data()); + auto* proj_vectors_data = reinterpret_cast*>(projected_vectors.data()); + auto* equiv_orientations_data = reinterpret_cast*>(equiv_orientations.data()); + unsigned int n_proj_vec = projected_vectors.shape(0); + unsigned int n_query_points = query_points.shape(0); + unsigned int n_equiv_orientations = equiv_orientations.shape(0); + local_bond_projection->compute(nq, orientations_data, query_points_data, n_query_points, proj_vectors_data, n_proj_vec, equiv_orientations_data, n_equiv_orientations, nlist, qargs); + } + +}; + +namespace detail { + +void export_LocalBondProjection(nb::module_& module) +{ + nb::class_(module, "LocalBondProjection") + .def(nb::init<>()) + .def("getNList", &LocalBondProjection::getNList) + .def("getProjections", &LocalBondProjection::getProjections) + .def("getNormedProjections", &LocalBondProjection::getNormedProjections) + .def("compute", &wrap::compute, nb::arg("nq"), nb::arg("orientations"), + nb::arg("query_points"), nb::arg("projected_vectors"), + nb::arg("equiv_orientations"), nb::arg("nlist").none(), + nb::arg("qargs")); +} + +}; }; // namespace detail +}; // namespace freud::locality diff --git a/freud/environment/export-LocalDescriptors.cc b/freud/environment/export-LocalDescriptors.cc new file mode 100644 index 000000000..15b56d2ff --- /dev/null +++ b/freud/environment/export-LocalDescriptors.cc @@ -0,0 +1,64 @@ +// 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 +#include +#include +#include // NOLINT(misc-include-cleaner): used implicitly +#include // NOLINT(misc-include-cleaner): used implicitly + +#include "LocalDescriptors.h" + + +namespace nb = nanobind; + +namespace freud { namespace environment { + +template +using nb_array = nanobind::ndarray; + +namespace wrap { +void compute(const std::shared_ptr& local_descriptors, + std::shared_ptr nq, + const nb_array>& query_points, + const unsigned int n_query_points, + const nb_array>& orientations, + std::shared_ptr nlist, + const locality::QueryArgs& qargs, + const unsigned int max_num_neighbors +) + { + auto* query_points_data = reinterpret_cast*>(query_points.data()); + auto* orientations_data = reinterpret_cast*>(orientations.data()); + local_descriptors->compute(nq, query_points_data, n_query_points, orientations_data, nlist, qargs, max_num_neighbors); + } + +}; + +namespace detail { + +void export_LocalDescriptors(nb::module_& module) +{ + + nb::enum_(module, "LocalDescriptorOrientation") + .value("LocalNeighborhood", LocalDescriptorOrientation::LocalNeighborhood) + .value("Global", LocalDescriptorOrientation::Global) + .value("ParticleLocal", LocalDescriptorOrientation::ParticleLocal) + .export_values(); + + nb::class_(module, "LocalDescriptors") + .def(nb::init()) + .def("getNList", &LocalDescriptors::getNList) + .def("getSph", &LocalDescriptors::getSph) + .def("getNSphs", &LocalDescriptors::getNSphs) + .def("getLMax", &LocalDescriptors::getLMax) + .def("getNegativeM", &LocalDescriptors::getNegativeM) + .def("getMode", &LocalDescriptors::getMode) + .def("compute", &wrap::compute,nb::arg("nq"), + nb::arg("query_points"), nb::arg("n_query_points"), nb::arg("orientations").none(), + nb::arg("nlist").none(), + nb::arg("qargs"), nb::arg("max_num_neighbors")); +} + +}; }; // namespace detail +}; // namespace freud::locality diff --git a/freud/environment/export-MatchEnv.cc b/freud/environment/export-MatchEnv.cc new file mode 100644 index 000000000..a0507f22a --- /dev/null +++ b/freud/environment/export-MatchEnv.cc @@ -0,0 +1,85 @@ +// 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 +#include +#include +#include // NOLINT(misc-include-cleaner): used implicitly +#include // NOLINT(misc-include-cleaner): used implicitly + +#include "MatchEnv.h" +#include "Registration.h" + + +namespace nb = nanobind; + +namespace freud { namespace environment { + +template +using nb_array = nanobind::ndarray; + +namespace wrap { +void compute_env_motif_match(const std::shared_ptr& env_motif_match, + std::shared_ptr nq, + std::shared_ptr nlist, + const locality::QueryArgs& qargs, + const nb_array>& motif, + const unsigned int motif_size, + const float threshold, + const bool registration +) +{ + auto* motif_data = reinterpret_cast*>(motif.data()); + env_motif_match->compute(nq, nlist, qargs, motif_data, motif_size, threshold, registration); +} + +void compute_env_rmsd_min(const std::shared_ptr& env_rmsd_min, + std::shared_ptr nq, + std::shared_ptr nlist, + const locality::QueryArgs& qargs, + const nb_array>& motif, + const unsigned int motif_size, + const float threshold, + const bool registration +) +{ + auto* motif_data = reinterpret_cast*>(motif.data()); + env_rmsd_min->compute(nq, nlist, qargs, motif_data, motif_size, threshold, registration); +} + +}; + +namespace detail { + +void export_MatchEnv(nb::module_& module) +{ + // export minimizeRMSD function, move convenience to wrap? TODO + nb::function("minimizeRMSD", &minimizeRMSD);//carefull ths fn is overloaded for easier python interactivity. You should use the one that takes box etc in. + // export isSimilar function, move convenience to wrap? TODO + nb::function("isSimilar", &isSimilar); //carefull ths fn is overloaded for easier python interactivity. You should use the one that takes box etc in. + + nb::class_(module, "MatchEnv") + .def(nb::init<>) + .def("getPointEnvironments", &MatchEnv::getPointEnvironments) + + nb::class_(module, "EnvironmentCluster") + .def(nb::init<>()) + .def("compute", &EnvironmentCluster::compute) + .def("getClusters", &EnvironmentCluster::getClusterIdx) + .def("getClusterEnvironments", &EnvironmentCluster::getClusterEnvironments) + .def("getNumClusters", &EnvironmentCluster::getNumClusters) + + nb::class_(module, "EnvironmentMotifMatch") + .def(nb::init<>()) + .def("compute", &wrap::compute_env_motif_match, nb::arg("nq"), nb::arg("nlist"), nb::arg("qargs"), nb::arg("motif"), nb::arg("motif_size"), nb::arg("threshold"), nb::arg("registration")) + .def("getMatches", &EnvironmentMotifMatch::getMatches) + + nb::class_(module, "EnvironmentRMSDMinimizer") + .def(nb::init<>()) + .def("compute", &wrap::compute_env_rmsd_min, nb::arg("nq"), nb::arg("nlist"), nb::arg("qargs"), nb::arg("motif"), nb::arg("motif_size"), nb::arg("threshold"), nb::arg("registration")) + .def("getRMSDs", &EnvironmentRMSDMinimizer::getRMSDs) + +} + +}; }; // namespace detail +}; // namespace freud::locality diff --git a/freud/environment/module-environment.cc b/freud/environment/module-environment.cc new file mode 100644 index 000000000..725cbf534 --- /dev/null +++ b/freud/environment/module-environment.cc @@ -0,0 +1,26 @@ +// 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 +#include + +namespace freud::environment::detail { + +void export_AngularSeparationNeighbor(nanobind::module_& m); +void export_AngularSeparationGlobal(nanobind::module_& m); +void export_LocalBondProjection(nanobind::module_& m); +void export_LocalDescriptors(nanobind::module_& m); +void export_BondOrder(nanobind::module_& m); +void export_MatchEnv(nanobind::module_& m); +}; // namespace freud::environment::detail +using namespace freud::environment::detail; + +NB_MODULE(_environment, module) // NOLINT(misc-use-anonymous-namespace): caused by nanobind +{ + export_AngularSeparationNeighbor(module); + export_AngularSeparationGlobal(module); + export_LocalBondProjection(module); + export_LocalDescriptors(module); + export_BondOrder(module); + export_MatchEnv(module); +} diff --git a/freud/locality.py b/freud/locality.py index 06114ade4..a66b61ef7 100644 --- a/freud/locality.py +++ b/freud/locality.py @@ -992,17 +992,18 @@ def box(self): @_Compute._computed_property def bin_counts(self): - """:class:`numpy.ndarray`: The bin counts in the histogram.""" + """:math:`\\left(N_0, N_1 \\right)` :class:`numpy.ndarray`: The bin counts in the histogram.""" # noqa: E501 return self._cpp_obj.getBinCounts().toNumpyArray() - @property + @property # TODO: Actual shape is 2, (d1=1, d2+1) and type is list def bin_centers(self): """:class:`numpy.ndarray`: The centers of each bin in the histogram (has the same shape as the histogram itself).""" centers = self._cpp_obj.getBinCenters() + # TODO: update docs to list the correct dimensions and type return [np.array(c) for c in centers] - @property + @property # TODO: Actual shape is 2, (d1=1, d2+1) and type is list def bin_edges(self): """:class:`numpy.ndarray`: The edges of each bin in the histogram (is one element larger in each dimension than the histogram because each diff --git a/freud/util/export-ManagedArray.cc b/freud/util/export-ManagedArray.cc index a8b8645a0..e681376bc 100644 --- a/freud/util/export-ManagedArray.cc +++ b/freud/util/export-ManagedArray.cc @@ -13,6 +13,7 @@ void export_ManagedArray(nanobind::module_& module) { // python wrapper classes for ManagedArray export_ManagedArray(module, "ManagedArray_float"); export_ManagedArray(module, "ManagedArray_double"); + export_ManagedArray>(module, "ManagedArray_complexfloat"); export_ManagedArray(module, "ManagedArray_unsignedint"); export_ManagedArray>(module, "ManagedArrayVec3_float"); export_ManagedArray>(module, "ManagedArray_complexfloat"); diff --git a/freud/util/export-ManagedArray.h b/freud/util/export-ManagedArray.h index 87d932c96..09a8cd9b7 100644 --- a/freud/util/export-ManagedArray.h +++ b/freud/util/export-ManagedArray.h @@ -7,6 +7,7 @@ #include "ManagedArray.h" #include "VectorMath.h" +#include #include #include #include diff --git a/tests/test_environment_bond_order.py b/tests/test_environment_bond_order.py index 09df84b9b..c3a787c62 100644 --- a/tests/test_environment_bond_order.py +++ b/tests/test_environment_bond_order.py @@ -40,6 +40,9 @@ def test_bond_order(self): # Test access bo.box bo.bond_order + bo.bin_counts + bo.bin_edges + bo.bin_centers # Test all the basic attributes. assert bo.nbins[0] == n_bins_theta @@ -57,8 +60,7 @@ def test_bond_order(self): box, positions, positions, "nearest", r_max, num_neighbors, True ) for nq, neighbors in test_set: - # Test that lbod gives identical results when orientations are the - # same. + # Test that lbod gives identical results when orientations are the same. # TODO: Find a way to test a rotated system to ensure that lbod gives # the desired results. bo = freud.environment.BondOrder(nbins, mode="lbod")