diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5ffa52fb1..2229cae2d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -125,7 +125,7 @@ jobs: gsd==${{ matrix.python.oldest_gsd }} matplotlib==${{ matrix.python.oldest_matplotlib }} # Test only the currently ported modules - CIBW_TEST_COMMAND: "cd {package}/tests && pytest test_box_box.py test_parallel.py test_locality_*.py test_data.py test_pmft.py test_util.py test_msd_msd.py test_interface.py test_cluster.py -v --log-level=DEBUG" + CIBW_TEST_COMMAND: "cd {package}/tests && pytest test_box_box.py test_parallel.py test_locality_*.py test_data.py test_pmft.py test_util.py test_msd_msd.py test_interface.py test_order_*.py test_cluster.py -v --log-level=DEBUG" # CIBW_TEST_COMMAND: "cd {package}/tests && pytest . -v --log-level=DEBUG" diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e2b0c5535..84d3259bd 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -68,6 +68,7 @@ jobs: pytest tests/test_interface.py -v pytest tests/test_msd_msd.py -v pytest tests/test_cluster.py -v + pytest tests/test_order_*.py -v # pytest tests/ -v tests_complete: diff --git a/.ruff.toml b/.ruff.toml index 537d89298..5a2d00f42 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -26,6 +26,8 @@ lint.ignore = [ "RUF012", # freud does not use typing hints "N802", # Allow names like Lx "NPY002", # TODO: Refactor benchmarks and tests to use numpy.random.Generator + "E741", + "E743" ] [lint.flake8-import-conventions] @@ -34,7 +36,6 @@ aliases = {} [lint.per-file-ignores] "tests/*.py" = ["PLR6301", # methods defined this way are used by pytest. - "E741", # l is an appropriate variable name for Qlm order parameters. "PLW2901", # TODO: Enable this check and fix after tests can be executed. "B018", # TODO: Enable this check and fix after tests can be executed. "PT011", # TODO: Enable this check and fix after tests can be executed. diff --git a/freud/CMakeLists.txt b/freud/CMakeLists.txt index 5cfaccaab..4af7371d0 100644 --- a/freud/CMakeLists.txt +++ b/freud/CMakeLists.txt @@ -89,23 +89,23 @@ add_library( ${VOROPP_SOURCE_DIR}/wall.cc ${VOROPP_SOURCE_DIR}/pre_container.cc ${VOROPP_SOURCE_DIR}/container_prd.cc - # order - # order/ContinuousCoordination.h - # order/ContinuousCoordination.cc - # order/Cubatic.cc - # order/Cubatic.h - # order/HexaticTranslational.cc - # order/HexaticTranslational.h - # order/Nematic.cc - # order/Nematic.h - # order/RotationalAutocorrelation.cc - # order/RotationalAutocorrelation.h - # order/SolidLiquid.cc - # order/SolidLiquid.h - # order/Steinhardt.cc - # order/Steinhardt.h - # order/Wigner3j.cc - # order/Wigner3j.h + order + order/ContinuousCoordination.h + order/ContinuousCoordination.cc + order/Cubatic.cc + order/Cubatic.h + order/HexaticTranslational.cc + order/HexaticTranslational.h + order/Nematic.cc + order/Nematic.h + order/RotationalAutocorrelation.cc + order/RotationalAutocorrelation.h + order/SolidLiquid.cc + order/SolidLiquid.h + order/Steinhardt.cc + order/Steinhardt.h + order/Wigner3j.cc + order/Wigner3j.h # parallel parallel/tbb_config.h parallel/tbb_config.cc @@ -173,6 +173,20 @@ target_set_install_rpath(_locality) # order nanobind_add_module(_order order/...) target_link_libraries(_order # PUBLIC freud TBB::tbb) target_set_install_rpath(_order) +# order +nanobind_add_module( + _order + order/module-order.cc + order/export-Nematic.cc + order/export-RotationalAutocorrelation.cc + order/export-Steinhardt.cc + order/export-SolidLiquid.cc + order/export-ContinuousCoordination.cc + order/export-Cubatic.cc + order/export-HexaticTranslational.cc) +target_link_libraries(_order PUBLIC freud) +target_set_install_rpath(_order) + # parallel nanobind_add_module(_parallel parallel/module-parallel.cc) target_link_libraries(_parallel PUBLIC freud TBB::tbb) @@ -201,12 +215,13 @@ set(python_files errors.py locality.py msd.py + order.py parallel.py pmft.py interface.py plot.py util.py) -# density.py diffraction.py environment.py order.py) +# cluster.py density.py diffraction.py environment.py interface.py msd.py) copy_files_to_build("${python_files}" "freud" "*.py") @@ -218,7 +233,7 @@ if(SKBUILD) # 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 _order DESTINATION freud) install(TARGETS _parallel DESTINATION freud) install(TARGETS _pmft DESTINATION freud) install(TARGETS _util DESTINATION freud) diff --git a/freud/__init__.py b/freud/__init__.py index f8d9f4809..7694cf787 100644 --- a/freud/__init__.py +++ b/freud/__init__.py @@ -1,8 +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. -# density,; diffraction,; environment,; order, -from . import box, cluster, data, interface, locality, msd, parallel, pmft +# cluster,; density,; diffraction,; environment,; interface,; msd,; +from . import box, cluster, data, interface, locality, msd, order, parallel, pmft from .box import Box from .locality import AABBQuery, LinkCell, NeighborList from .parallel import NumThreads, get_num_threads, set_num_threads @@ -24,7 +24,7 @@ "interface", "locality", "msd", - # "order", + "order", "parallel", "pmft", "Box", diff --git a/freud/locality/NeighborComputeFunctional.h b/freud/locality/NeighborComputeFunctional.h index 1c45725e7..3f06dd7af 100644 --- a/freud/locality/NeighborComputeFunctional.h +++ b/freud/locality/NeighborComputeFunctional.h @@ -40,7 +40,7 @@ std::shared_ptr makeDefaultNlist(const std::shared_ptr& nlist, size_t point_index) : NeighborPerPointIterator(point_index), m_nlist(nlist) { m_current_index = m_nlist->find_first_index(point_index); @@ -76,8 +76,8 @@ class NeighborListPerPointIterator : public NeighborPerPointIterator } private: - const NeighborList* m_nlist; //! The NeighborList being iterated over. - size_t m_current_index; //! The row of m_nlist where the iterator is currently located. + const std::shared_ptr m_nlist; //! The NeighborList being iterated over. + size_t m_current_index; //! The row of m_nlist where the iterator is currently located. size_t m_returned_point_index { 0xffffffff}; //! The index of the last returned point (i.e. the value of //! m_nlist.getNeighbors()(m_current_index, 0)). Initialized to an arbitrary sentinel in @@ -110,9 +110,10 @@ class NeighborListPerPointIterator : public NeighborPerPointIterator * input. It should implement iteration logic over the iterator. */ template -void loopOverNeighborsIterator(const NeighborQuery* neighbor_query, const vec3* query_points, - unsigned int n_query_points, QueryArgs qargs, const NeighborList* nlist, - const ComputePairType& cf, bool parallel = true) +void loopOverNeighborsIterator(const std::shared_ptr& neighbor_query, + const vec3* query_points, unsigned int n_query_points, QueryArgs qargs, + const std::shared_ptr& nlist, const ComputePairType& cf, + bool parallel = true) { // check if nlist exists if (nlist != nullptr) @@ -235,7 +236,8 @@ void loopOverNeighbors(const std::shared_ptr& neighbor_query, con * input. It should implement iteration logic over the iterator. */ template -void loopOverNeighborListIterator(const NeighborList* nlist, const ComputePairType& cf, bool parallel = true) +void loopOverNeighborListIterator(const std::shared_ptr& nlist, const ComputePairType& cf, + bool parallel = true) { util::forLoopWrapper( 0, nlist->getNumQueryPoints(), diff --git a/freud/order.pyx b/freud/order.py similarity index 69% rename from freud/order.pyx rename to freud/order.py index 7100d919a..8fffd3a0d 100644 --- a/freud/order.pyx +++ b/freud/order.py @@ -10,36 +10,23 @@ Fourier Transforms. """ -from freud.util cimport _Compute, quat, vec3 - -from freud.errors import FreudDeprecationWarning - -from cython.operator cimport dereference - -from freud.locality cimport _PairCompute - import collections.abc import logging import time import warnings +from math import floor import numpy as np +import freud._order import freud.locality - -cimport numpy as np - -cimport freud._order -cimport freud.locality -cimport freud.util +from freud.locality import _PairCompute +from freud.util import _Compute logger = logging.getLogger(__name__) -# 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 Cubatic(_Compute): +class Cubatic(_Compute): r"""Compute the cubatic order parameter :cite:`Haji_Akbari_2015` for a system of particles using simulated annealing instead of Newton-Raphson root finding. @@ -56,18 +43,15 @@ seed (unsigned int, optional): Random seed to use in calculations. If :code:`None`, system time is used. (Default value = :code:`None`). - """ # noqa: E501 - cdef freud._order.Cubatic * thisptr + """ - def __cinit__(self, t_initial, t_final, scale, n_replicates=1, seed=None): + def __init__(self, t_initial, t_final, scale, n_replicates=1, seed=None): if seed is None: seed = int(time.time()) - self.thisptr = new freud._order.Cubatic( - t_initial, t_final, scale, n_replicates, seed) - - def __dealloc__(self): - del self.thisptr + self._cpp_obj = freud._order.Cubatic( + t_initial, t_final, scale, n_replicates, seed + ) def compute(self, orientations): r"""Calculates the per-particle and global order parameter. @@ -76,90 +60,75 @@ def compute(self, orientations): orientations ((:math:`N_{particles}`, 4) :class:`numpy.ndarray`): Orientations as angles to use in computation. """ - orientations = freud.util._convert_array( - orientations, shape=(None, 4)) - - cdef const float[:, ::1] l_orientations = orientations - cdef unsigned int num_particles = l_orientations.shape[0] - - self.thisptr.compute( - &l_orientations[0, 0], num_particles) + orientations = freud.util._convert_array(orientations, shape=(None, 4)) + self._cpp_obj.compute(orientations) return self @property def t_initial(self): """float: The value of the initial temperature.""" - return self.thisptr.getTInitial() + return self._cpp_obj.getTInitial() @property def t_final(self): """float: The value of the final temperature.""" - return self.thisptr.getTFinal() + return self._cpp_obj.getTFinal() @property def scale(self): """float: The scale.""" - return self.thisptr.getScale() + return self._cpp_obj.getScale() @property def n_replicates(self): """unsigned int: Number of replicate simulated annealing runs.""" - return self.thisptr.getNReplicates() + return self._cpp_obj.getNReplicates() @property def seed(self): """unsigned int: Random seed to use in calculations.""" - return self.thisptr.getSeed() + return self._cpp_obj.getSeed() @_Compute._computed_property def order(self): """float: Cubatic order parameter of the system.""" - return self.thisptr.getCubaticOrderParameter() + return self._cpp_obj.getCubaticOrderParameter() @_Compute._computed_property def orientation(self): """:math:`\\left(4 \\right)` :class:`numpy.ndarray`: The quaternion of global orientation.""" - cdef quat[float] q = self.thisptr.getCubaticOrientation() - return np.asarray([q.s, q.v.x, q.v.y, q.v.z], dtype=np.float32) + q = self._cpp_obj.getCubaticOrientation() + return np.asarray(q, dtype=np.float32) @_Compute._computed_property def particle_order(self): """:math:`\\left(N_{particles} \\right)` :class:`numpy.ndarray`: Order parameter.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getParticleOrderParameter(), - freud.util.arr_type_t.FLOAT) + return self._cpp_obj.getParticleOrderParameter().toNumpyArray() @_Compute._computed_property def global_tensor(self): """:math:`\\left(3, 3, 3, 3 \\right)` :class:`numpy.ndarray`: Rank 4 tensor corresponding to the global orientation. Computed from all orientations.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getGlobalTensor(), - freud.util.arr_type_t.FLOAT) + return self._cpp_obj.getGlobalTensor().toNumpyArray() @_Compute._computed_property def cubatic_tensor(self): """:math:`\\left(3, 3, 3, 3 \\right)` :class:`numpy.ndarray`: Rank 4 homogeneous tensor representing the optimal system-wide coordinates.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getCubaticTensor(), - freud.util.arr_type_t.FLOAT) + return self._cpp_obj.getCubaticTensor().toNumpyArray() def __repr__(self): - return ("freud.order.{cls}(t_initial={t_initial}, t_final={t_final}, " - "scale={scale}, n_replicates={n_replicates}, " - "seed={seed})").format(cls=type(self).__name__, - t_initial=self.t_initial, - t_final=self.t_final, - scale=self.scale, - n_replicates=self.n_replicates, - seed=self.seed) + return ( + f"freud.order.{type(self).__name__}(t_initial={self.t_initial}, " + f"t_final={self.t_final}, scale={self.scale}, " + f"n_replicates={self.n_replicates}, seed={self.seed})" + ) -cdef class Nematic(_Compute): +class Nematic(_Compute): r"""Compute the nematic order parameter for a system of particles. Note: @@ -183,16 +152,12 @@ def __repr__(self): freud.order.Nematic() """ - cdef freud._order.Nematic *thisptr - def __cinit__(self): - self.thisptr = new freud._order.Nematic() - - def __dealloc__(self): - del self.thisptr + def __init__(self): + self._cpp_obj = freud._order.Nematic() def compute(self, orientations): - r"""Calculates the per-particle and global order parameter. + """Calculates the per-particle and global order parameter. Example:: @@ -204,60 +169,58 @@ def compute(self, orientations): 1.0 Args: - orientations (:math:`\left(N_{particles}, 3 \right)` :class:`numpy.ndarray`): + orientations \ + (:math:`\\left(N_{particles}, 3 \\right)` :class:`numpy.ndarray`): Orientation vectors for which to calculate the order parameter. - """ # noqa: E501 + """ if orientations.shape[1] == 4: - raise ValueError('In freud versions >=3.0.0, Nematic.compute() takes ' - '3d orientation vectors instead of 4d quaternions.') - orientations = freud.util._convert_array( - orientations, shape=(None, 3)) - - if len(np.where(~orientations.any(axis=1))[0])!=0: - warnings.warn('Including zero vector in the orientations array ' - 'may lead to undefined behavior.', - UserWarning) - cdef const float[:, ::1] l_orientations = orientations - cdef unsigned int num_particles = l_orientations.shape[0] - - self.thisptr.compute( &l_orientations[0, 0], - num_particles) + msg = ( + "In freud versions >=3.0.0, Nematic.compute() takes " + "3d orientation vectors instead of 4d quaternions." + ) + raise ValueError(msg) + orientations = freud.util._convert_array(orientations, shape=(None, 3)) + + if len(np.where(~orientations.any(axis=1))[0]) != 0: + warnings.warn( + "Including zero vector in the orientations array " + "may lead to undefined behavior.", + UserWarning, + stacklevel=2, + ) + + self._cpp_obj.compute(orientations) return self @_Compute._computed_property def order(self): """float: Nematic order parameter of the system.""" - return self.thisptr.getNematicOrderParameter() + return self._cpp_obj.getNematicOrderParameter() @_Compute._computed_property def director(self): """:math:`\\left(3 \\right)` :class:`numpy.ndarray`: The average nematic director.""" - cdef vec3[float] n = self.thisptr.getNematicDirector() - return np.asarray([n.x, n.y, n.z], dtype=np.float32) + return np.asarray([*self._cpp_obj.getNematicDirector()], dtype=np.float32) @_Compute._computed_property def particle_tensor(self): """:math:`\\left(N_{particles}, 3, 3 \\right)` :class:`numpy.ndarray`: - One 3x3 matrix per-particle corresponding to each individual - particle orientation.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getParticleTensor(), - freud.util.arr_type_t.FLOAT) + One 3x3 matrix per-particle corresponding to each individual + particle orientation.""" + return self._cpp_obj.getParticleTensor().toNumpyArray() @_Compute._computed_property def nematic_tensor(self): """:math:`\\left(3, 3 \\right)` :class:`numpy.ndarray`: 3x3 matrix corresponding to the average particle orientation.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getNematicTensor(), - freud.util.arr_type_t.FLOAT) + return self._cpp_obj.getNematicTensor().toNumpyArray() def __repr__(self): - return "freud.order.{cls}()".format(cls=type(self).__name__) + return f"freud.order.{type(self).__name__}()" -cdef class Hexatic(_PairCompute): +class Hexatic(_PairCompute): r"""Calculates the :math:`k`-atic order parameter for 2D systems. The :math:`k`-atic order parameter (called the hexatic order parameter for @@ -306,14 +269,10 @@ def __repr__(self): Voronoi neighbor list, this results in the 2D Minkowski Structure Metrics :math:`\psi'_k` :cite:`Mickel2013` (Default value = :code:`False`). - """ # noqa: E501 - cdef freud._order.Hexatic * thisptr - - def __cinit__(self, k=6, weighted=False): - self.thisptr = new freud._order.Hexatic(k, weighted) + """ - def __dealloc__(self): - del self.thisptr + def __init__(self, k=6, weighted=False): + self._cpp_obj = freud._order.Hexatic(k, weighted) def compute(self, system, neighbors=None): r"""Calculates the hexatic order parameter. @@ -339,18 +298,12 @@ def compute(self, system, neighbors=None): `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, neighbors=neighbors) - self.thisptr.compute(nlist.get_ptr(), - nq.get_ptr(), dereference(qargs.thisptr)) + """ + + nq, nlist, qargs, l_query_points, num_query_points = self._preprocess_arguments( + system, neighbors=neighbors + ) + self._cpp_obj.compute(nlist._cpp_obj, nq._cpp_obj, qargs._cpp_obj) return self @property @@ -363,23 +316,23 @@ def default_query_args(self): def particle_order(self): """:math:`\\left(N_{particles} \\right)` :class:`numpy.ndarray`: Order parameter.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getOrder(), - freud.util.arr_type_t.COMPLEX_FLOAT) + return self._cpp_obj.getOrder().toNumpyArray() @property def k(self): """unsigned int: Symmetry of the order parameter.""" - return self.thisptr.getK() + return self._cpp_obj.getK() @property def weighted(self): """bool: Whether neighbor weights were used in the computation.""" - return self.thisptr.isWeighted() + return self._cpp_obj.isWeighted() def __repr__(self): - return "freud.order.{cls}(k={k}, weighted={weighted})".format( - cls=type(self).__name__, k=self.k, weighted=self.weighted) + return ( + f"freud.order.{type(self).__name__}(k={self.k}, " + f"weighted={self.weighted})" + ) def plot(self, ax=None): """Plot order parameter distribution. @@ -393,26 +346,29 @@ def plot(self, ax=None): (:class:`matplotlib.axes.Axes`): Axis with the plot. """ import freud.plot + xlabel = r"$\left|\psi{prime}_{k}\right|$".format( - prime='\'' if self.weighted else '', - k=self.k) + prime="'" if self.weighted else "", k=self.k + ) return freud.plot.histogram_plot( np.absolute(self.particle_order), title="Hexatic Order Parameter " + xlabel, xlabel=xlabel, ylabel=r"Number of particles", - ax=ax) + 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 Steinhardt(_PairCompute): +class Steinhardt(_PairCompute): r"""Compute one or more of the rotationally invariant Steinhardt order parameter :math:`q_l` or :math:`w_l` for a set of points :cite:`Steinhardt:1983aa`. @@ -524,48 +480,41 @@ def _repr_png_(self): wl_normalize (bool, optional): Determines whether to normalize the :math:`w_l` version of the Steinhardt order parameter (Default value = :code:`False`). - """ # noqa: E501 - cdef freud._order.Steinhardt * thisptr + """ - def __cinit__(self, l, average=False, wl=False, weighted=False, - wl_normalize=False): + def __init__(self, l, average=False, wl=False, weighted=False, wl_normalize=False): if not isinstance(l, collections.abc.Sequence): l = [l] if len(l) == 0: - raise ValueError("At least one l must be specified.") - self.thisptr = new freud._order.Steinhardt(l, average, wl, weighted, - wl_normalize) - - def __dealloc__(self): - del self.thisptr + msg = "At least one l must be specified." + raise ValueError(msg) + self._cpp_obj = freud._order.Steinhardt(l, average, wl, weighted, wl_normalize) @property def average(self): """bool: Whether the averaged Steinhardt order parameter was calculated.""" - return self.thisptr.isAverage() + return self._cpp_obj.isAverage() @property def wl(self): """bool: Whether the :math:`w_l` version of the Steinhardt order parameter was used.""" - return self.thisptr.isWl() + return self._cpp_obj.isWl() @property def weighted(self): """bool: Whether neighbor weights were used in the computation.""" - return self.thisptr.isWeighted() + return self._cpp_obj.isWeighted() @property def wl_normalize(self): - return self.thisptr.isWlNormalized() + return self._cpp_obj.isWlNormalized() @property - def l(self): # noqa: E743 + def l(self): """unsigned int: Spherical harmonic quantum number l.""" - # list conversion is necessary as otherwise CI Cython complains about - # compiling the below expression with two different types. - ls = list(self.thisptr.getL()) + ls = self._cpp_obj.getL() return ls[0] if len(ls) == 1 else ls @_Compute._computed_property @@ -575,9 +524,7 @@ def order(self): :math:`\overline{q}_{lm}` values if ``average`` is enabled) over all particles before computing the rotationally-invariant order parameter.""" - # list conversion is necessary as otherwise CI Cython complains about - # compiling the below expression with two different types. - order = list(self.thisptr.getOrder()) + order = self._cpp_obj.getOrder() return order[0] if len(order) == 1 else order @_Compute._computed_property @@ -585,11 +532,8 @@ def particle_order(self): """:math:`\\left(N_{particles}, N_l \\right)` :class:`numpy.ndarray`: Variant of the Steinhardt order parameter for each particle (filled with :code:`nan` for particles with no neighbors).""" - array = freud.util.make_managed_numpy_array( - &self.thisptr.getParticleOrder(), freud.util.arr_type_t.FLOAT) - if array.shape[1] == 1: - return np.ravel(array) - return array + p_orders = self._cpp_obj.getParticleOrder().toNumpyArray() + return np.ravel(p_orders) if p_orders.shape[1] == 1 else p_orders @_Compute._computed_property def ql(self): @@ -599,23 +543,15 @@ def ql(self): no matter which other options are selected. It obeys the ``weighted`` argument but otherwise returns the "plain" :math:`q_l` regardless of ``average``, ``wl``, ``wl_normalize``.""" - array = freud.util.make_managed_numpy_array( - &self.thisptr.getQl(), freud.util.arr_type_t.FLOAT) - if array.shape[1] == 1: - return np.ravel(array) - return array + ql = self._cpp_obj.getQl().toNumpyArray() + return np.ravel(ql) if ql.shape[1] == 1 else ql @_Compute._computed_property def particle_harmonics(self): """:math:`\\left(N_{particles}, 2l+1\\right)` :class:`numpy.ndarray`: The raw array of :math:`q_{lm}(i)`. The array is provided in the order given by fsph: :math:`m = 0, 1, ..., l, -1, ..., -l`.""" - qlm_arrays = self.thisptr.getQlm() - # Since Cython does not really support const iteration, we must iterate - # using range and not use the for array in qlm_arrays style for loop. - qlm_list = [freud.util.make_managed_numpy_array( - &qlm_arrays[i], freud.util.arr_type_t.COMPLEX_FLOAT) - for i in range(qlm_arrays.size())] + qlm_list = [qlm.toNumpyArray() for qlm in self._cpp_obj.getQlm()] return qlm_list if len(qlm_list) > 1 else qlm_list[0] def compute(self, system, neighbors=None): @@ -638,31 +574,22 @@ def compute(self, system, neighbors=None): `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, neighbors=neighbors) - - self.thisptr.compute(nlist.get_ptr(), - nq.get_ptr(), - dereference(qargs.thisptr)) + """ + + # Call the pair compute setup function + nq, nlist, qargs, l_query_points, num_query_points = self._preprocess_arguments( + system, neighbors=neighbors + ) + + self._cpp_obj.compute(nlist._cpp_obj, nq._cpp_obj, qargs._cpp_obj) return self def __repr__(self): - return ("freud.order.{cls}(l={l}, average={average}, wl={wl}, " - "weighted={weighted}, wl_normalize={wl_normalize})").format( - cls=type(self).__name__, - l=self.l, # noqa: 743 - average=self.average, - wl=self.wl, - weighted=self.weighted, - wl_normalize=self.wl_normalize) + return ( + f"freud.order.{type(self).__name__}(l={self.l}, " + f"average={self.average}, wl={self.wl}, weighted={self.weighted}, " + f"wl_normalize={self.wl_normalize})" + ) def plot(self, ax=None): """Plot order parameter distribution. @@ -681,15 +608,15 @@ def plot(self, ax=None): if not isinstance(ls, list): ls = [ls] + weighted_str = "'" if self.weighted else "" legend_labels = [ - r"${mode_letter}{prime}_{{{sph_l}{average}}}$".format( - mode_letter='w' if self.wl else 'q', - prime='\'' if self.weighted else '', - sph_l=sph_l, - average=',ave' if self.average else '') + ( + f"${'w' if self.wl else 'q'}{weighted_str}_" + f"{{{sph_l}{',ave' if self.average else ''}}}$" + ) for sph_l in ls ] - xlabel = ', '.join(legend_labels) + xlabel = ", ".join(legend_labels) # Don't print legend if only one l requested. if len(legend_labels) == 1: @@ -701,17 +628,19 @@ def plot(self, ax=None): xlabel=xlabel, ylabel=r"Number of particles", ax=ax, - legend_labels=legend_labels) + legend_labels=legend_labels, + ) def _repr_png_(self): try: import freud.plot + return freud.plot._ax_to_bytes(self.plot()) except (AttributeError, ImportError): return None -cdef class SolidLiquid(_PairCompute): +class SolidLiquid(_PairCompute): r"""Identifies solid-like clusters using dot products of :math:`q_{lm}`. The solid-liquid order parameter :cite:`Wolde:1995aa,Filion_2010` uses a @@ -748,15 +677,19 @@ def _repr_png_(self): normalize_q (bool): Whether to normalize the dot product (Default value = :code:`True`). - """ # noqa: E501 - cdef freud._order.SolidLiquid * thisptr - - def __cinit__(self, l, q_threshold, solid_threshold, normalize_q=True): - self.thisptr = new freud._order.SolidLiquid( - l, q_threshold, solid_threshold, normalize_q) + """ - def __dealloc__(self): - del self.thisptr + def __init__(self, l, q_threshold, solid_threshold, normalize_q=True): + if not isinstance(solid_threshold, int): + warning_text = ( + "solid_threshold should be an integer, and will be rounded down" + f" (got {solid_threshold})" + ) + warnings.warn(warning_text, RuntimeWarning, stacklevel=2) + solid_threshold = floor(solid_threshold) + self._cpp_obj = freud._order.SolidLiquid( + l, q_threshold, solid_threshold, normalize_q + ) def compute(self, system, neighbors=None): r"""Compute the order parameter. @@ -772,102 +705,82 @@ def compute(self, system, neighbors=None): `_ (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) - self.thisptr.compute(nlist.get_ptr(), - nq.get_ptr(), - dereference(qargs.thisptr)) + + nq, nlist, qargs, l_query_points, num_query_points = self._preprocess_arguments( + system, neighbors=neighbors + ) + self._cpp_obj.compute(nlist._cpp_obj, nq._cpp_obj, qargs._cpp_obj) return self @property - def l(self): # noqa: E743 + def l(self): """unsigned int: Spherical harmonic quantum number l.""" - return self.thisptr.getL() + return self._cpp_obj.getL() @property def q_threshold(self): """float: Value of dot product threshold.""" - return self.thisptr.getQThreshold() + return self._cpp_obj.getQThreshold() @property def solid_threshold(self): """float: Value of number-of-bonds threshold.""" - return self.thisptr.getSolidThreshold() + return self._cpp_obj.getSolidThreshold() @property def normalize_q(self): """bool: Whether the dot product is normalized.""" - return self.thisptr.getNormalizeQ() + return self._cpp_obj.getNormalizeQ() @_Compute._computed_property def cluster_idx(self): """:math:`\\left(N_{particles}\\right)` :class:`numpy.ndarray`: Solid-like cluster indices for each particle.""" - 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 ql_ij(self): """:math:`\\left(N_{bonds}\\right)` :class:`numpy.ndarray`: Bond dot products :math:`q_l(i, j)`. Indexed by the elements of :code:`self.nlist`.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getQlij(), - freud.util.arr_type_t.FLOAT) + return self._cpp_obj.getQlij().toNumpyArray() @_Compute._computed_property def particle_harmonics(self): """:math:`\\left(N_{particles}, 2*l+1\\right)` :class:`numpy.ndarray`: The raw array of \\overline{q}_{lm}(i). The array is provided in the order given by fsph: :math:`m = 0, 1, ..., l, -1, ..., -l`.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getQlm(), - freud.util.arr_type_t.COMPLEX_FLOAT) + return self._cpp_obj.getQlm().toNumpyArray() @_Compute._computed_property def cluster_sizes(self): """:math:`(N_{clusters}, )` :class:`np.ndarray`: The sizes of all clusters.""" - return np.asarray(self.thisptr.getClusterSizes()) + return self._cpp_obj.getClusterSizes().toNumpyArray() @_Compute._computed_property def largest_cluster_size(self): """unsigned int: The largest cluster size.""" - return self.thisptr.getLargestClusterSize() + return self._cpp_obj.getLargestClusterSize() @_Compute._computed_property def nlist(self): """:class:`freud.locality.NeighborList`: Neighbor list of solid-like bonds.""" - nlist = freud.locality._nlist_from_cnlist(self.thisptr.getNList()) - nlist._compute = self - return nlist + return freud.locality._nlist_from_cnlist(self._cpp_obj.getNList()) @_Compute._computed_property def num_connections(self): """:math:`\\left(N_{particles}\\right)` :class:`numpy.ndarray`: The number of solid-like bonds for each particle.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getNumberOfConnections(), - freud.util.arr_type_t.UNSIGNED_INT) + return self._cpp_obj.getNumberOfConnections().toNumpyArray() def __repr__(self): - return ("freud.order.{cls}(l={sph_l}, q_threshold={q_threshold}, " - "solid_threshold={solid_threshold}, " - "normalize_q={normalize_q})").format( - cls=type(self).__name__, - sph_l=self.l, - q_threshold=self.q_threshold, - solid_threshold=self.solid_threshold, - normalize_q=self.normalize_q) + return ( + f"freud.order.{type(self).__name__}(l={self.l}, " + f"q_threshold={self.q_threshold}, solid_threshold={self.solid_threshold}, " + f"normalize_q={self.normalize_q})" + ) def plot(self, ax=None): """Plot solid-like cluster distribution. @@ -881,23 +794,26 @@ 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 else: return freud.plot.clusters_plot( - values, counts, num_clusters_to_plot=10, ax=ax) + 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 RotationalAutocorrelation(_Compute): +class RotationalAutocorrelation(_Compute): """Calculates a measure of total rotational autocorrelation. For any calculation of rotational correlations of extended (i.e. non-point) @@ -926,16 +842,12 @@ def _repr_png_(self): Order of the hyperspherical harmonic. Must be a positive, even integer. """ - cdef freud._order.RotationalAutocorrelation * thisptr - def __cinit__(self, l): + def __init__(self, l): if l % 2 or l < 0: - raise ValueError( - "The quantum number must be a positive, even integer.") - self.thisptr = new freud._order.RotationalAutocorrelation(l) - - def __dealloc__(self): - del self.thisptr + msg = "The quantum number must be a positive, even integer." + raise ValueError(msg) + self._cpp_obj = freud._order.RotationalAutocorrelation(l) def compute(self, ref_orientations, orientations): """Calculates the rotational autocorrelation function for a single frame. @@ -945,47 +857,35 @@ def compute(self, ref_orientations, orientations): Orientations for the initial frame. orientations ((:math:`N_{orientations}`, 4) :class:`numpy.ndarray`): Orientations for the frame of interest. - """ # noqa - ref_orientations = freud.util._convert_array( - ref_orientations, shape=(None, 4)) - orientations = freud.util._convert_array( - orientations, shape=ref_orientations.shape) - - cdef const float[:, ::1] l_ref_orientations = ref_orientations - cdef const float[:, ::1] l_orientations = orientations - cdef unsigned int nP = orientations.shape[0] - - self.thisptr.compute( - &l_ref_orientations[0, 0], - &l_orientations[0, 0], - nP) + """ + assert len(ref_orientations) == len( + orientations + ), "orientations and ref_orientations must have the same shape." + self._cpp_obj.compute(ref_orientations, orientations) return self @_Compute._computed_property def order(self): """float: Autocorrelation of the system.""" - return self.thisptr.getRotationalAutocorrelation() + return self._cpp_obj.getRotationalAutocorrelation() @_Compute._computed_property def particle_order(self): """(:math:`N_{orientations}`) :class:`numpy.ndarray`: Rotational autocorrelation values calculated for each orientation.""" - return freud.util.make_managed_numpy_array( - &self.thisptr.getRAArray(), - freud.util.arr_type_t.COMPLEX_FLOAT) + return self._cpp_obj.getRAArray().toNumpyArray() @property - def l(self): # noqa: E743 + def l(self): """int: The azimuthal quantum number, which defines the order of the hyperspherical harmonic.""" - return self.thisptr.getL() + return self._cpp_obj.getL() def __repr__(self): - return "freud.order.{cls}(l={sph_l})".format(cls=type(self).__name__, - sph_l=self.l) + return f"freud.order.{type(self).__name__}(l={self.l})" -cdef class ContinuousCoordination(_PairCompute): +class ContinuousCoordination(_PairCompute): r"""Computes the continuous local coordination number. The :class:`ContinuousCoordination` class implements extensions of the Voronoi @@ -1034,16 +934,13 @@ def __repr__(self): coordination number. (Default value: :code:`True`) """ - cdef freud._order.ContinuousCoordination* thisptr - def __cinit__(self, powers=None, compute_log=True, compute_exp=True): + def __init__(self, powers=None, compute_log=True, compute_exp=True): if powers is None: powers = [2.0] - self.thisptr = new freud._order.ContinuousCoordination( - powers, compute_log, compute_exp) - - def __dealloc__(self): - del self.thisptr + self._cpp_obj = freud._order.ContinuousCoordination( + powers, compute_log, compute_exp + ) def compute(self, system=None, voronoi=None): r"""Calculates the local coordination number for the specified points. @@ -1067,17 +964,17 @@ def compute(self, system=None, voronoi=None): assumed to have been computed already, and system is ignored. (Default value: None). """ - cdef freud.locality.Voronoi cpp_voronoi if system is None and voronoi is None: - raise ValueError("Must specify system or voronoi.") + msg = "Must specify system or voronoi." + raise ValueError(msg) if voronoi is None: voronoi = freud.locality.Voronoi() voronoi.compute(system) elif not hasattr(voronoi, "nlist"): - raise RuntimeError( - "Must call compute on Voronoi object prior to computing coordination.") + msg = "Must call compute on Voronoi object prior to computing coordination." + raise RuntimeError(msg) cpp_voronoi = voronoi - self.thisptr.compute(cpp_voronoi.thisptr) + self._cpp_obj.compute(cpp_voronoi._cpp_obj) return self @_Compute._computed_property @@ -1096,9 +993,7 @@ def coordination(self): Coordination numbers are in order of selected powers, log, and exp. """ - return freud.util.make_managed_numpy_array( - &self.thisptr.getCoordination(), - freud.util.arr_type_t.FLOAT) + return self._cpp_obj.getCoordination().toNumpyArray() @property def powers(self): @@ -1107,30 +1002,25 @@ def powers(self): Changes to this property are not reflected when computing coordination numbers. """ - return self.thisptr.getPowers() + return self._cpp_obj.getPowers() @property def compute_log(self): """bool: Whether to compute the log continuous coordination number.""" - return self.thisptr.getComputeLog() + return self._cpp_obj.getComputeLog() @property def compute_exp(self): """bool: Whether to compute the exponential coordination number.""" - return self.thisptr.getComputeExp() + return self._cpp_obj.getComputeExp() @property def number_of_coordinations(self): """int: The number of coordination numbers computed.""" - return self.thisptr.getNumberOfCoordinations() + return self._cpp_obj.getNumberOfCoordinations() def __repr__(self): return ( - "freud.order.{cls}(powers={powers}, " - "compute_log={compute_log}, compute_exp={compute_exp})" - ).format( - cls=type(self).__name__, - powers=self.powers, - compute_log=self.compute_log, - compute_exp=self.compute_exp, + f"freud.order.{type(self).__name__}(powers={self.powers}, " + f"compute_log={self.compute_log}, compute_exp={self.compute_exp})" ) diff --git a/freud/order/ContinuousCoordination.cc b/freud/order/ContinuousCoordination.cc index 370479eff..f83832794 100644 --- a/freud/order/ContinuousCoordination.cc +++ b/freud/order/ContinuousCoordination.cc @@ -18,11 +18,12 @@ ContinuousCoordination::ContinuousCoordination(std::vector powers, bool c : m_powers(std::move(powers)), m_compute_exp(compute_exp), m_compute_log(compute_log) {} -void ContinuousCoordination::compute(const freud::locality::Voronoi* voronoi) +void ContinuousCoordination::compute(const std::shared_ptr& voronoi) { auto nlist = voronoi->getNeighborList(); size_t num_points = nlist->getNumQueryPoints(); - m_coordination.prepare({num_points, getNumberOfCoordinations()}); + m_coordination = std::make_shared>( + std::vector {num_points, getNumberOfCoordinations()}); const auto& volumes = voronoi->getVolumes(); const auto& num_neighbors = nlist->getCounts(); // This is necessary as the current Windows runners on GitHub actions have a @@ -32,37 +33,37 @@ void ContinuousCoordination::compute(const freud::locality::Voronoi* voronoi) // 2 for triangles 3 for pyramids const float volume_prefactor = voronoi->getBox().is2D() ? 2.0 : 3.0; freud::locality::loopOverNeighborListIterator( - nlist.get(), + nlist, [&](size_t particle_index, const std::shared_ptr& ppiter) { // 1/2 comes from the distance vector since we want to measure from the pyramid // base to the center. const float prefactor - = 1.0F / (volume_prefactor * 2.0F * static_cast(volumes[particle_index])); + = 1.0F / (volume_prefactor * 2.0F * static_cast((*volumes)[particle_index])); std::vector i_volumes; for (freud::locality::NeighborBond nb = ppiter->next(); !ppiter->end(); nb = ppiter->next()) { i_volumes.emplace_back(prefactor * nb.getWeight() * nb.getDistance()); } size_t coordination_number {0}; - float num_neighbors_i {static_cast(num_neighbors[particle_index])}; + float num_neighbors_i {static_cast((*num_neighbors)[particle_index])}; for (size_t k {0}; k < powers.size(); ++k) { float coordination = std::transform_reduce( i_volumes.begin(), i_volumes.end(), 0.0F, std::plus<>(), [&powers, k](const auto& volume) { return std::pow(volume, powers[k]); }); - m_coordination(particle_index, coordination_number++) + (*m_coordination)(particle_index, coordination_number++) = std::pow(num_neighbors_i, 2.0F - powers[k]) / coordination; } if (m_compute_log) { float coordination = std::transform_reduce(i_volumes.begin(), i_volumes.end(), 0.0F, std::plus<>(), logf); - m_coordination(particle_index, coordination_number++) + (*m_coordination)(particle_index, coordination_number++) = -coordination / std::log(num_neighbors_i); } if (m_compute_exp) { - m_coordination(particle_index, coordination_number) = std::transform_reduce( + (*m_coordination)(particle_index, coordination_number) = std::transform_reduce( i_volumes.begin(), i_volumes.end(), 0.0F, std::plus<>(), [num_neighbors_i](const auto& volume) { return std::exp(volume - (1.0F / static_cast(num_neighbors_i))); diff --git a/freud/order/ContinuousCoordination.h b/freud/order/ContinuousCoordination.h index 1a3a89bfb..0ccc42da1 100644 --- a/freud/order/ContinuousCoordination.h +++ b/freud/order/ContinuousCoordination.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include "ManagedArray.h" @@ -25,7 +26,7 @@ class ContinuousCoordination ~ContinuousCoordination() = default; //! Compute the local continuous coordination number - void compute(const freud::locality::Voronoi* voronoi); + void compute(const std::shared_ptr& voronoi); //! Get the powers of the continuous coordination number to compute const std::vector& getPowers() const @@ -43,8 +44,8 @@ class ContinuousCoordination return m_compute_exp; } - //! Get a reference to the last computed number of neighbors - const util::ManagedArray& getCoordination() const + //! Get a shared pointer to the last computed number of neighbors + const std::shared_ptr> getCoordination() const { return m_coordination; } @@ -53,10 +54,10 @@ class ContinuousCoordination unsigned int getNumberOfCoordinations() const; private: - std::vector m_powers; //!< The powers to use for CNv - bool m_compute_log; //!< Whether to compute CNlog - bool m_compute_exp; //!< Whether to compute CNexp - util::ManagedArray m_coordination; //!< number of neighbors array computed + std::vector m_powers; //!< The powers to use for CNv + bool m_compute_log; //!< Whether to compute CNlog + bool m_compute_exp; //!< Whether to compute CNexp + std::shared_ptr> m_coordination; //!< number of neighbors array computed }; }; }; // namespace freud::order diff --git a/freud/order/Cubatic.cc b/freud/order/Cubatic.cc index aeadf4b34..605af7e75 100644 --- a/freud/order/Cubatic.cc +++ b/freud/order/Cubatic.cc @@ -2,7 +2,7 @@ // This file is from the freud project, released under the BSD 3-Clause License. #include -#include +#include #include #include "Cubatic.h" @@ -71,7 +71,7 @@ tensor4 tensor4::operator*(const float& b) const void tensor4::copyToManagedArray(util::ManagedArray& ma) { - std::copy(data.begin(), data.end(), ma.get()); + std::copy(data.begin(), data.end(), ma.data()); } //! Complete tensor contraction. @@ -236,12 +236,12 @@ tensor4 Cubatic::calculateGlobalTensor(quat* orientations) const void Cubatic::compute(quat* orientations, unsigned int num_orientations) { m_n = num_orientations; - m_particle_order_parameter.prepare(m_n); + m_particle_order_parameter = std::make_shared>(std::vector {m_n}); // Calculate the per-particle tensor tensor4 global_tensor = calculateGlobalTensor(orientations); - m_global_tensor.prepare({3, 3, 3, 3}); - global_tensor.copyToManagedArray(m_global_tensor); + m_global_tensor = std::make_shared>(std::vector {3, 3, 3, 3}); + global_tensor.copyToManagedArray((*m_global_tensor)); // The paper recommends using a Newton-Raphson scheme to optimize the order // parameter, but in practice we find that simulated annealing performs @@ -329,8 +329,8 @@ void Cubatic::compute(quat* orientations, unsigned int num_orientations) } } - m_cubatic_tensor.prepare({3, 3, 3, 3}); - p_cubatic_tensor[max_idx].copyToManagedArray(m_cubatic_tensor); + m_cubatic_tensor = std::make_shared>(std::vector {3, 3, 3, 3}); + p_cubatic_tensor[max_idx].copyToManagedArray((*m_cubatic_tensor)); m_cubatic_orientation = p_cubatic_orientation[max_idx]; m_cubatic_order_parameter = p_cubatic_order_parameter[max_idx]; @@ -341,7 +341,7 @@ void Cubatic::compute(quat* orientations, unsigned int num_orientations) // The per-particle order parameter is defined as the value of the // cubatic order parameter if the global orientation was the // particle orientation, so we can reuse the same machinery. - m_particle_order_parameter[i] + (*m_particle_order_parameter)[i] = calcCubaticOrderParameter(calcCubaticTensor(orientations[i]), global_tensor); } }); diff --git a/freud/order/Cubatic.h b/freud/order/Cubatic.h index fabcfef8e..cef8ae784 100644 --- a/freud/order/Cubatic.h +++ b/freud/order/Cubatic.h @@ -7,7 +7,7 @@ #include "ManagedArray.h" #include "VectorMath.h" #include -#include +#include /*! \file Cubatic.h \brief Compute the cubatic order parameter for each particle. @@ -78,17 +78,17 @@ class Cubatic return m_cubatic_order_parameter; } - const util::ManagedArray& getParticleOrderParameter() const + const std::shared_ptr>& getParticleOrderParameter() const { return m_particle_order_parameter; } - const util::ManagedArray& getGlobalTensor() const + const std::shared_ptr>& getGlobalTensor() const { return m_global_tensor; } - const util::ManagedArray& getCubaticTensor() const + const std::shared_ptr>& getCubaticTensor() const { return m_cubatic_tensor; } @@ -180,10 +180,12 @@ class Cubatic tensor4 m_gen_r4_tensor; //!< The sum of various products of Kronecker deltas that is stored as a member //!< for convenient reuse. - util::ManagedArray m_particle_order_parameter; //!< The per-particle value of the order parameter. - util::ManagedArray + std::shared_ptr> + m_particle_order_parameter; //!< The per-particle value of the order parameter. + std::shared_ptr> m_global_tensor; //!< The system-averaged homogeneous tensor encoding all particle orientations. - util::ManagedArray m_cubatic_tensor; //!< The output tensor computed via simulated annealing. + std::shared_ptr> + m_cubatic_tensor; //!< The output tensor computed via simulated annealing. std::array, 3> m_system_vectors; //!< The global coordinate system, always use a simple Euclidean basis. diff --git a/freud/order/HexaticTranslational.cc b/freud/order/HexaticTranslational.cc index ba6c932c0..c2080d10c 100644 --- a/freud/order/HexaticTranslational.cc +++ b/freud/order/HexaticTranslational.cc @@ -2,22 +2,26 @@ // This file is from the freud project, released under the BSD 3-Clause License. #include "HexaticTranslational.h" +#include "ManagedArray.h" +#include "NeighborList.h" +#include "NeighborPerPointIterator.h" +#include "NeighborQuery.h" namespace freud { namespace order { //! Compute the order parameter template template -void HexaticTranslational::computeGeneral(Func func, const freud::locality::NeighborList* nlist, - const freud::locality::NeighborQuery* points, - freud::locality::QueryArgs qargs, bool normalize_by_k) +void HexaticTranslational::computeGeneral(Func func, const std::shared_ptr nlist, + const std::shared_ptr& points, + const freud::locality::QueryArgs qargs, bool normalize_by_k) { const auto box = points->getBox(); box.enforce2D(); const unsigned int Np = points->getNPoints(); - m_psi_array.prepare(Np); + m_psi_array = std::make_shared>>(std::vector {Np}); freud::locality::loopOverNeighborsIterator( points, points->getPoints(), Np, qargs, nlist, @@ -32,24 +36,25 @@ void HexaticTranslational::computeGeneral(Func func, const freud::locality::N const float weight(m_weighted ? nb.getWeight() : 1.0); // Compute psi for this vector - m_psi_array[i] += weight * func(delta); + (*m_psi_array)[i] += weight * func(delta); total_weight += weight; } if (normalize_by_k) { - m_psi_array[i] /= std::complex(m_k); + (*m_psi_array)[i] /= std::complex(m_k); } else { - m_psi_array[i] /= std::complex(total_weight); + (*m_psi_array)[i] /= std::complex(total_weight); } }); } Hexatic::Hexatic(unsigned int k, bool weighted) : HexaticTranslational(k, weighted) {} -void Hexatic::compute(const freud::locality::NeighborList* nlist, - const freud::locality::NeighborQuery* points, freud::locality::QueryArgs qargs) +void Hexatic::compute(std::shared_ptr nlist, + const std::shared_ptr& points, + const freud::locality::QueryArgs& qargs) { computeGeneral( [this](const vec3& delta) { diff --git a/freud/order/HexaticTranslational.h b/freud/order/HexaticTranslational.h index 3651ee6af..21d000896 100644 --- a/freud/order/HexaticTranslational.h +++ b/freud/order/HexaticTranslational.h @@ -42,7 +42,7 @@ template class HexaticTranslational } //! Get a reference to the order parameter array - const util::ManagedArray>& getOrder() const + const std::shared_ptr>> getOrder() const { return m_psi_array; } @@ -50,14 +50,15 @@ template class HexaticTranslational protected: //! Compute the order parameter template - void computeGeneral(Func func, const freud::locality::NeighborList* nlist, - const freud::locality::NeighborQuery* points, freud::locality::QueryArgs qargs, - bool normalize_by_k); + void computeGeneral(Func func, const std::shared_ptr nlist, + // const vec3* points, + const std::shared_ptr& points, + const freud::locality::QueryArgs qargs, bool normalize_by_k); const T m_k; //!< The symmetry order for Hexatic, or normalization for Translational const bool m_weighted; //!< Whether to use neighbor weights in computing the order parameter (default false) - util::ManagedArray> m_psi_array; //!< psi array computed + std::shared_ptr>> m_psi_array; //!< psi array computed }; //! Compute the hexatic order parameter for a set of points @@ -73,8 +74,10 @@ class Hexatic : public HexaticTranslational ~Hexatic() override = default; //! Compute the hexatic order parameter - void compute(const freud::locality::NeighborList* nlist, const freud::locality::NeighborQuery* points, - freud::locality::QueryArgs qargs); + + void compute(std::shared_ptr nlist, + const std::shared_ptr& points, + const freud::locality::QueryArgs& qargs); }; }; }; // end namespace freud::order diff --git a/freud/order/Nematic.cc b/freud/order/Nematic.cc index cf8473d12..cc1e26d02 100644 --- a/freud/order/Nematic.cc +++ b/freud/order/Nematic.cc @@ -1,6 +1,7 @@ // 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 "Nematic.h" @@ -17,12 +18,12 @@ float Nematic::getNematicOrderParameter() const return m_nematic_order_parameter; } -const util::ManagedArray& Nematic::getParticleTensor() const +const std::shared_ptr> Nematic::getParticleTensor() const { return m_particle_tensor; } -const util::ManagedArray& Nematic::getNematicTensor() const +const std::shared_ptr> Nematic::getNematicTensor() const { return m_nematic_tensor; } @@ -40,8 +41,8 @@ vec3 Nematic::getNematicDirector() const void Nematic::compute(vec3* orientations, unsigned int n) { m_n = n; - m_particle_tensor.prepare({m_n, 3, 3}); - m_nematic_tensor_local.reset(); + m_particle_tensor = std::make_shared>(std::vector {m_n, 3, 3}); + m_nematic_tensor_local->reset(); // calculate per-particle tensor util::forLoopWrapper(0, n, [&](size_t begin, size_t end) { @@ -68,28 +69,28 @@ void Nematic::compute(vec3* orientations, unsigned int n) { for (unsigned int k = 0; k < 3; k++) { - m_particle_tensor(i, j, k) += Q_ab(j, k); - m_nematic_tensor_local.local()(j, k) += Q_ab(j, k); + (*m_particle_tensor)(i, j, k) += Q_ab(j, k); + m_nematic_tensor_local->local()(j, k) += Q_ab(j, k); } } } }); // Now calculate the sum of Q_ab's - m_nematic_tensor.prepare({3, 3}); - m_nematic_tensor_local.reduceInto(m_nematic_tensor); + m_nematic_tensor = std::make_shared>(std::vector {3, 3}); + m_nematic_tensor_local->reduceInto(*m_nematic_tensor); // Normalize by the number of particles - for (unsigned int i = 0; i < m_nematic_tensor.size(); ++i) + for (unsigned int i = 0; i < m_nematic_tensor->size(); ++i) { - m_nematic_tensor[i] /= static_cast(m_n); + (*m_nematic_tensor)[i] /= static_cast(m_n); } // the order parameter is the eigenvector belonging to the largest eigenvalue util::ManagedArray eval = util::ManagedArray(3); util::ManagedArray evec = util::ManagedArray({3, 3}); - freud::util::diagonalize33SymmetricMatrix(m_nematic_tensor, eval, evec); + freud::util::diagonalize33SymmetricMatrix(*m_nematic_tensor, eval, evec); m_nematic_director = vec3(evec(2, 0), evec(2, 1), evec(2, 2)); m_nematic_order_parameter = eval[2]; } diff --git a/freud/order/Nematic.h b/freud/order/Nematic.h index 6de0d3b80..55463c69e 100644 --- a/freud/order/Nematic.h +++ b/freud/order/Nematic.h @@ -23,7 +23,11 @@ class Nematic { public: //! Constructor - Nematic() = default; + Nematic() + { + m_nematic_tensor = std::make_shared>(std::vector {3, 3}); + m_nematic_tensor_local = std::make_shared>(std::vector {3, 3}); + } //! Destructor virtual ~Nematic() = default; @@ -34,9 +38,9 @@ class Nematic //! Get the value of the last computed nematic order parameter float getNematicOrderParameter() const; - const util::ManagedArray& getParticleTensor() const; + const std::shared_ptr> getParticleTensor() const; - const util::ManagedArray& getNematicTensor() const; + const std::shared_ptr> getNematicTensor() const; unsigned int getNumParticles() const; @@ -47,9 +51,10 @@ class Nematic float m_nematic_order_parameter {0}; //!< Current value of the order parameter vec3 m_nematic_director; //!< The director (eigenvector corresponding to the OP) - util::ManagedArray m_nematic_tensor {{3, 3}}; //!< The computed nematic tensor. - util::ThreadStorage m_nematic_tensor_local {{3, 3}}; //!< Thread-specific nematic tensor. - util::ManagedArray m_particle_tensor; //!< The per-particle tensor that is summed up to Q. + std::shared_ptr> m_nematic_tensor; //!< The computed nematic tensor. + std::shared_ptr> m_nematic_tensor_local; //!< Thread-specific nematic tensor. + std::shared_ptr> + m_particle_tensor; //!< The per-particle tensor that is summed up to Q. }; }; }; // end namespace freud::order diff --git a/freud/order/RotationalAutocorrelation.cc b/freud/order/RotationalAutocorrelation.cc index cf48be4e9..056c11bc0 100644 --- a/freud/order/RotationalAutocorrelation.cc +++ b/freud/order/RotationalAutocorrelation.cc @@ -3,6 +3,7 @@ #include "RotationalAutocorrelation.h" +#include "VectorMath.h" #include "utils.h" #include @@ -43,9 +44,9 @@ inline std::complex RotationalAutocorrelation::hypersphere_harmonic(const std::complex sum_tracker(0, 0); for (unsigned int k = (m1 + m2 < m_l ? 0 : m1 + m2 - m_l); k <= std::min(m1, m2); k++) { - float fact_product = static_cast(m_factorials[k]) - * static_cast(m_factorials[m_l + k - m1 - m2]) * static_cast(m_factorials[m1 - k]) - * static_cast(m_factorials[m2 - k]); + float fact_product = static_cast((*m_factorials)[k]) + * static_cast((*m_factorials)[m_l + k - m1 - m2]) + * static_cast((*m_factorials)[m1 - k]) * static_cast((*m_factorials)[m2 - k]); sum_tracker += cpow(xi_conj, k) * cpow(zeta, m2 - k) * cpow(zeta_conj, m1 - k) * cpow(-xi, m_l + k - m1 - m2) / fact_product; } @@ -55,7 +56,7 @@ inline std::complex RotationalAutocorrelation::hypersphere_harmonic(const void RotationalAutocorrelation::compute(const quat* ref_orientations, const quat* orientations, unsigned int N) { - m_RA_array.prepare(N); + m_RA_array = std::make_shared>>(N); // Precompute the hyperspherical harmonics for the unit quaternion. The // default quaternion constructor gives a unit quaternion. We will assume @@ -70,8 +71,8 @@ void RotationalAutocorrelation::compute(const quat* ref_orientations, con for (unsigned int b = 0; b <= m_l; b++) { unit_harmonics.push_back(std::conj(hypersphere_harmonic(xi, zeta, a, b))); - prefactors[a][b] = static_cast(m_factorials[a] * m_factorials[m_l - a] * m_factorials[b] - * m_factorials[m_l - b]) + prefactors[a][b] = static_cast((*m_factorials)[a] * (*m_factorials)[m_l - a] + * (*m_factorials)[b] * (*m_factorials)[m_l - b]) / (static_cast(m_l + 1)); } } @@ -86,7 +87,7 @@ void RotationalAutocorrelation::compute(const quat* ref_orientations, con std::complex zeta = std::complex(qq_1.v.z, qq_1.s); // Loop through the valid quantum numbers. - m_RA_array[i] = std::complex(0, 0); + (*m_RA_array)[i] = std::complex(0, 0); unsigned int uh_index = 0; for (unsigned int a = 0; a <= m_l; a++) { @@ -94,7 +95,7 @@ void RotationalAutocorrelation::compute(const quat* ref_orientations, con { std::complex combined_value = unit_harmonics[uh_index] * hypersphere_harmonic(xi, zeta, a, b); - m_RA_array[i] += prefactors[a][b] * combined_value; + (*m_RA_array)[i] += prefactors[a][b] * combined_value; uh_index += 1; } } @@ -104,7 +105,7 @@ void RotationalAutocorrelation::compute(const quat* ref_orientations, con float RA_sum(0); for (unsigned int i = 0; i < N; i++) { - RA_sum += std::real(m_RA_array[i]); + RA_sum += std::real((*m_RA_array)[i]); } m_Ft = RA_sum / static_cast(N); }; diff --git a/freud/order/RotationalAutocorrelation.h b/freud/order/RotationalAutocorrelation.h index f2062e0d3..bce29f132 100644 --- a/freud/order/RotationalAutocorrelation.h +++ b/freud/order/RotationalAutocorrelation.h @@ -5,6 +5,7 @@ #define ROTATIONAL_AUTOCORRELATION_H #include +#include #include "ManagedArray.h" #include "VectorMath.h" @@ -54,11 +55,11 @@ class RotationalAutocorrelation { // For efficiency, we precompute all required factorials for use during // the per-particle computation. - m_factorials.prepare(m_l + 1); - m_factorials[0] = 1; + m_factorials = std::make_shared>(m_l + 1); + (*m_factorials)[0] = 1; for (unsigned int i = 1; i <= m_l; i++) { - m_factorials[i] = i * m_factorials[i - 1]; + (*m_factorials)[i] = i * (*m_factorials)[i - 1]; } } @@ -72,7 +73,7 @@ class RotationalAutocorrelation } //! Get a reference to the last computed rotational autocorrelation array. - const util::ManagedArray>& getRAArray() const + const std::shared_ptr>>& getRAArray() const { return m_RA_array; } @@ -120,8 +121,8 @@ class RotationalAutocorrelation unsigned int m_l; //!< Order of the hyperspherical harmonic. float m_Ft {0}; //!< Real value of calculated RA function. - util::ManagedArray> m_RA_array; //!< Array of RA values per particle - util::ManagedArray m_factorials; //!< Array of cached factorials + std::shared_ptr>> m_RA_array; //!< Array of RA values per particle + std::shared_ptr> m_factorials; //!< Array of cached factorials }; }; }; // end namespace freud::order diff --git a/freud/order/SolidLiquid.cc b/freud/order/SolidLiquid.cc index 330f4d19a..c1f793116 100644 --- a/freud/order/SolidLiquid.cc +++ b/freud/order/SolidLiquid.cc @@ -4,6 +4,7 @@ #include #include "NeighborComputeFunctional.h" +#include "NeighborList.h" #include "SolidLiquid.h" namespace freud { namespace order { @@ -19,49 +20,50 @@ SolidLiquid::SolidLiquid(unsigned int l, float q_threshold, unsigned int solid_t } } -void SolidLiquid::compute(const freud::locality::NeighborList* nlist, - const freud::locality::NeighborQuery* points, freud::locality::QueryArgs qargs) +void SolidLiquid::compute(const std::shared_ptr& nlist, + const std::shared_ptr& points, + freud::locality::QueryArgs& qargs) { // This function requires a NeighborList object, so we always make one and store it locally. m_nlist = locality::makeDefaultNlist(points, nlist, points->getPoints(), points->getNPoints(), qargs); - const unsigned int num_query_points(m_nlist.getNumQueryPoints()); + const unsigned int num_query_points(m_nlist->getNumQueryPoints()); // Compute Steinhardt using neighbor list (also gets ql for normalization) - m_steinhardt.compute(&m_nlist, points, qargs); + m_steinhardt.compute(m_nlist, points, qargs); // SolidLiquid only has one l value so we index the 2D array from Steinhardt. const auto& qlm = m_steinhardt.getQlm()[0]; const auto& ql = m_steinhardt.getQl(); // Compute (normalized) dot products for each bond in the neighbor list const auto normalizationfactor = float(4.0 * M_PI / m_num_ms); - const unsigned int num_bonds(m_nlist.getNumBonds()); - m_ql_ij.prepare(num_bonds); + const unsigned int num_bonds(m_nlist->getNumBonds()); + m_ql_ij = std::make_shared>(std::vector {num_bonds}); util::forLoopWrapper( 0, num_query_points, [&](size_t begin, size_t end) { for (unsigned int i = begin; i != end; ++i) { - unsigned int bond(m_nlist.find_first_index(i)); - for (; bond < num_bonds && m_nlist.getNeighbors()(bond, 0) == i; ++bond) + unsigned int bond(m_nlist->find_first_index(i)); + for (; bond < num_bonds && (*m_nlist->getNeighbors())(bond, 0) == i; ++bond) { - const unsigned int j(m_nlist.getNeighbors()(bond, 1)); + const unsigned int j((*m_nlist->getNeighbors())(bond, 1)); // Accumulate the dot product over m of qlmi and qlmj vectors std::complex bond_ql_ij = 0; for (unsigned int k = 0; k < m_num_ms; k++) { - bond_ql_ij += qlm(i, k) * std::conj(qlm(j, k)); + bond_ql_ij += (*qlm)(i, k) * std::conj((*qlm)(j, k)); } // Optionally normalize dot products by points' ql values, // accounting for the normalization of ql values if (m_normalize_q) { - bond_ql_ij *= normalizationfactor / (ql[i] * ql[j]); + bond_ql_ij *= normalizationfactor / ((*ql)[i] * (*ql)[j]); } - m_ql_ij[bond] = bond_ql_ij.real(); + (*m_ql_ij)[bond] = bond_ql_ij.real(); } } }, @@ -71,16 +73,18 @@ void SolidLiquid::compute(const freud::locality::NeighborList* nlist, std::vector solid_filter(num_bonds); for (unsigned int bond(0); bond < num_bonds; bond++) { - solid_filter[bond] = (m_ql_ij[bond] > m_q_threshold); + solid_filter[bond] = ((*m_ql_ij)[bond] > m_q_threshold); } - freud::locality::NeighborList solid_nlist(m_nlist); + freud::locality::NeighborList solid_nlist(*m_nlist); solid_nlist.filter(solid_filter.cbegin()); // Save the neighbor counts of solid-like bonds for each query point - m_number_of_connections.prepare(num_query_points); + m_number_of_connections + = std::make_shared>(std::vector {num_query_points}); + for (unsigned int i(0); i < num_query_points; i++) { - m_number_of_connections[i] = solid_nlist.getCounts()[i]; + (*m_number_of_connections)[i] = (*solid_nlist.getCounts())[i]; } // Filter nlist to only bonds between solid-like particles @@ -89,16 +93,16 @@ void SolidLiquid::compute(const freud::locality::NeighborList* nlist, std::vector neighbor_count_filter(num_solid_bonds); for (unsigned int bond(0); bond < num_solid_bonds; bond++) { - const unsigned int i(solid_nlist.getNeighbors()(bond, 0)); - const unsigned int j(solid_nlist.getNeighbors()(bond, 1)); - neighbor_count_filter[bond] = (m_number_of_connections[i] >= m_solid_threshold - && m_number_of_connections[j] >= m_solid_threshold); + const unsigned int i((*solid_nlist.getNeighbors())(bond, 0)); + const unsigned int j((*solid_nlist.getNeighbors())(bond, 1)); + neighbor_count_filter[bond] = ((*m_number_of_connections)[i] >= m_solid_threshold + && (*m_number_of_connections)[j] >= m_solid_threshold); } - freud::locality::NeighborList& solid_neighbor_nlist(solid_nlist); + freud::locality::NeighborList solid_neighbor_nlist(solid_nlist); solid_neighbor_nlist.filter(neighbor_count_filter.cbegin()); // Find clusters of solid-like particles - m_cluster.compute(points, &solid_neighbor_nlist, qargs); + m_cluster.compute(points, std::make_shared(solid_neighbor_nlist), qargs); } }; }; // end namespace freud::order diff --git a/freud/order/SolidLiquid.h b/freud/order/SolidLiquid.h index 65e080e70..cf19a3dca 100644 --- a/freud/order/SolidLiquid.h +++ b/freud/order/SolidLiquid.h @@ -85,8 +85,9 @@ class SolidLiquid } //! Compute the Solid-Liquid Order Parameter - void compute(const freud::locality::NeighborList* nlist, const freud::locality::NeighborQuery* points, - freud::locality::QueryArgs qargs); + void compute(const std::shared_ptr& nlist, + const std::shared_ptr& points, + freud::locality::QueryArgs& qargs); //! Returns largest cluster size. unsigned int getLargestClusterSize() const @@ -107,13 +108,13 @@ class SolidLiquid //! Get a reference to the last computed set of solid-like cluster // indices for each particle - const util::ManagedArray& getClusterIdx() const + const std::shared_ptr> getClusterIdx() const { return m_cluster.getClusterIdx(); } //! Get a reference to the number of connections per particle - const util::ManagedArray& getNumberOfConnections() const + const std::shared_ptr> getNumberOfConnections() const { return m_number_of_connections; } @@ -124,37 +125,38 @@ class SolidLiquid } //! 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; } //! Get the last calculated qlm for each particle - const util::ManagedArray>& getQlm() const + const std::shared_ptr>> getQlm() const { return m_steinhardt.getQlm()[0]; } //! Return the ql_ij values. - const util::ManagedArray& getQlij() const + const std::shared_ptr> getQlij() const { return m_ql_ij; } private: - unsigned int m_l; //!< Value of l for the spherical harmonic. - unsigned int m_num_ms; //!< The number of magnetic quantum numbers (2*m_l+1). - float m_q_threshold; //!< Dot product cutoff - unsigned int m_solid_threshold; //!< Solid-like num connections cutoff - bool m_normalize_q; //!< Whether to normalize the qlmi dot products. - locality::NeighborList m_nlist; //!< The NeighborList used in the last call to compute. + unsigned int m_l; //!< Value of l for the spherical harmonic. + unsigned int m_num_ms; //!< The number of magnetic quantum numbers (2*m_l+1). + float m_q_threshold; //!< Dot product cutoff + unsigned int m_solid_threshold; //!< Solid-like num connections cutoff + bool m_normalize_q; //!< Whether to normalize the qlmi dot products. + std::shared_ptr m_nlist; //!< The NeighborList used in the last call to compute. freud::order::Steinhardt m_steinhardt; //!< Steinhardt class used to compute qlm freud::cluster::Cluster m_cluster; //!< Cluster class used to cluster solid-like bonds - util::ManagedArray m_ql_ij; //!< All of the qlmi dot qlmj's computed - util::ManagedArray m_number_of_connections; //! Number of connections for each particle with - //! dot product above q_threshold + std::shared_ptr> m_ql_ij; //!< All of the qlmi dot qlmj's computed + std::shared_ptr> + m_number_of_connections; //! Number of connections for each particle with + //! dot product above q_threshold }; }; }; // end namespace freud::order diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index 6d7350e5d..1039ca1f3 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -2,6 +2,7 @@ // This file is from the freud project, released under the BSD 3-Clause License. #include "Steinhardt.h" +#include "ManagedArray.h" #include "NeighborComputeFunctional.h" #include "utils.h" #include @@ -40,30 +41,34 @@ void Steinhardt::reallocateArrays(unsigned int Np) const auto num_ls = m_ls.size(); - m_qli.prepare({Np, num_ls}); + m_qli = std::make_shared>(std::vector {Np, num_ls}); if (m_average) { - m_qliAve.prepare({Np, num_ls}); + m_qliAve = std::make_shared>(std::vector {Np, num_ls}); } if (m_wl) { - m_wli.prepare({Np, num_ls}); + m_wli = std::make_shared>(std::vector {Np, num_ls}); } for (size_t l_index = 0; l_index < m_ls.size(); ++l_index) { const auto num_ms = m_num_ms[l_index]; - m_qlmi[l_index].prepare({Np, num_ms}); - m_qlm[l_index].prepare(num_ms); + m_qlmi[l_index] + = std::make_shared>>(std::vector {Np, num_ms}); + m_qlm[l_index] + = std::make_shared>>(std::vector {num_ms}); if (m_average) { - m_qlmiAve[l_index].prepare({Np, num_ms}); + m_qlmiAve[l_index] + = std::make_shared>>(std::vector {Np, num_ms}); } } } -void Steinhardt::compute(const freud::locality::NeighborList* nlist, - const freud::locality::NeighborQuery* points, freud::locality::QueryArgs qargs) +void Steinhardt::compute(const std::shared_ptr& nlist, + const std::shared_ptr& points, + const freud::locality::QueryArgs& qargs) { // Allocate and zero out arrays as necessary. reallocateArrays(points->getNPoints()); @@ -79,7 +84,7 @@ void Steinhardt::compute(const freud::locality::NeighborList* nlist, // Reduce qlm for (size_t l_index = 0; l_index < m_ls.size(); ++l_index) { - m_qlm_local[l_index].reduceInto(m_qlm[l_index]); + m_qlm_local[l_index].reduceInto((*m_qlm[l_index])); } if (m_wl) @@ -96,8 +101,9 @@ void Steinhardt::compute(const freud::locality::NeighborList* nlist, m_norm = normalizeSystem(); } -void Steinhardt::baseCompute(const freud::locality::NeighborList* nlist, - const freud::locality::NeighborQuery* points, freud::locality::QueryArgs qargs) +void Steinhardt::baseCompute(const std::shared_ptr& nlist, + const std::shared_ptr& points, + const freud::locality::QueryArgs& qargs) { std::vector normalizationfactor(m_ls.size()); for (size_t l_index = 0; l_index < m_ls.size(); ++l_index) @@ -159,10 +165,10 @@ void Steinhardt::baseCompute(const freud::locality::NeighborList* nlist, const auto& Ylm = Ylms[l_index]; // Get the initial index and iterate using ++ for faster iteration // Profiling showed using operator() to slow the code significantly. - const size_t index = qlmi.getIndex({i, 0}); + const size_t index = qlmi->getIndex({i, 0}); for (size_t k = 0; k < m_num_ms[l_index]; ++k) { - qlmi[index + k] += weight * Ylm[k]; + (*qlmi)[index + k] += weight * Ylm[k]; } } // Accumulate weight for normalization @@ -170,13 +176,13 @@ void Steinhardt::baseCompute(const freud::locality::NeighborList* nlist, } // End loop going over neighbor bonds // Normalize! - const size_t qli_i_start = m_qli.getIndex({i, 0}); + const size_t qli_i_start = m_qli->getIndex({i, 0}); for (size_t l_index = 0; l_index < m_ls.size(); ++l_index) { // get l specific vectors/arrays auto& qlmi = m_qlmi[l_index]; auto& qlm_local = m_qlm_local[l_index]; - const size_t first_qlmi_index = qlmi.getIndex({i, 0}); + const size_t first_qlmi_index = qlmi->getIndex({i, 0}); const size_t qli_index = qli_i_start + l_index; for (size_t k = 0; k < m_num_ms[l_index]; ++k) @@ -184,23 +190,24 @@ void Steinhardt::baseCompute(const freud::locality::NeighborList* nlist, // Cache the index for efficiency. const size_t qlmi_index = first_qlmi_index + k; - qlmi[qlmi_index] /= total_weight; + (*qlmi)[qlmi_index] /= total_weight; // Add the norm, which is the (complex) squared magnitude - m_qli[qli_index] += norm(qlmi[qlmi_index]); + (*m_qli)[qli_index] += norm((*qlmi)[qlmi_index]); // This array gets populated by computeAve in the averaging case. if (!m_average) { - qlm_local.local()[k] += qlmi[qlmi_index] / float(m_Np); + qlm_local.local()[k] += (*qlmi)[qlmi_index] / float(m_Np); } } - m_qli[qli_index] *= normalizationfactor[l_index]; - m_qli[qli_index] = std::sqrt(m_qli[qli_index]); + (*m_qli)[qli_index] *= normalizationfactor[l_index]; + (*m_qli)[qli_index] = std::sqrt((*m_qli)[qli_index]); } }); } -void Steinhardt::computeAve(const freud::locality::NeighborList* nlist, - const freud::locality::NeighborQuery* points, freud::locality::QueryArgs qargs) +void Steinhardt::computeAve(const std::shared_ptr& nlist, + const std::shared_ptr& points, + const freud::locality::QueryArgs& qargs) { std::shared_ptr iter; if (nlist == nullptr) @@ -224,14 +231,14 @@ void Steinhardt::computeAve(const freud::locality::NeighborList* nlist, { auto& qlmiAve = m_qlmiAve[l_index]; auto& qlmi = m_qlmi[l_index]; - const auto ave_index = qlmiAve.getIndex({i, 0}); - const auto nb_index = qlmi.getIndex({nb.getPointIdx(), 0}); + const auto ave_index = qlmiAve->getIndex({i, 0}); + const auto nb_index = qlmi->getIndex({nb.getPointIdx(), 0}); for (size_t k = 0; k < m_num_ms[l_index]; ++k) { // Adding all the qlm of the neighbors. We use the // vector function signature for indexing into the // arrays for speed. - qlmiAve[ave_index + k] += qlmi[nb_index + k]; + (*qlmiAve)[ave_index + k] += (*qlmi)[nb_index + k]; } } neighborcount++; @@ -239,13 +246,13 @@ void Steinhardt::computeAve(const freud::locality::NeighborList* nlist, // Normalize! - const size_t qliAve_i_start = m_qliAve.getIndex({i, 0}); + const size_t qliAve_i_start = m_qliAve->getIndex({i, 0}); for (size_t l_index = 0; l_index < m_ls.size(); ++l_index) { auto& qlmiAve = m_qlmiAve[l_index]; auto& qlmi = m_qlmi[l_index]; auto& qlm_local = m_qlm_local[l_index]; - const size_t first_qlmi_index = qlmiAve.getIndex({i, 0}); + const size_t first_qlmi_index = qlmiAve->getIndex({i, 0}); const size_t qliAve_index = qliAve_i_start + l_index; for (size_t k = 0; k < m_num_ms[l_index]; ++k) @@ -253,14 +260,14 @@ void Steinhardt::computeAve(const freud::locality::NeighborList* nlist, // Cache the index for efficiency. const size_t qlmi_index = first_qlmi_index + k; // Add the qlm of the particle i itself - qlmiAve[qlmi_index] += qlmi[qlmi_index]; - qlmiAve[qlmi_index] /= static_cast(neighborcount); - qlm_local.local()[k] += qlmiAve[qlmi_index] / float(m_Np); + (*qlmiAve)[qlmi_index] += (*qlmi)[qlmi_index]; + (*qlmiAve)[qlmi_index] /= static_cast(neighborcount); + qlm_local.local()[k] += (*qlmiAve)[qlmi_index] / float(m_Np); // Add the norm, which is the complex squared magnitude - m_qliAve[qliAve_index] += norm(qlmiAve[qlmi_index]); + (*m_qliAve)[qliAve_index] += norm((*qlmiAve)[qlmi_index]); } - m_qliAve[qliAve_index] *= normalizationfactor[l_index]; - m_qliAve[qliAve_index] = std::sqrt(m_qliAve[qliAve_index]); + (*m_qliAve)[qliAve_index] *= normalizationfactor[l_index]; + (*m_qliAve)[qliAve_index] = std::sqrt((*m_qliAve)[qliAve_index]); } }); } @@ -277,14 +284,14 @@ std::vector Steinhardt::normalizeSystem() for (size_t k = 0; k < m_num_ms[l_index]; ++k) { // Add the norm, which is the complex squared magnitude - calc_norm += norm(qlm[k]); + calc_norm += norm((*qlm)[k]); } const float ql_system_norm = std::sqrt(calc_norm * normalizationfactor); if (m_wl) { const auto wigner3j_values = getWigner3j(l); - float wl_system_norm = reduceWigner3j(qlm.get(), l, wigner3j_values); + float wl_system_norm = reduceWigner3j(qlm.get()->data(), l, wigner3j_values); // The normalization factor of wl is calculated using qli, which is // equivalent to calculate the normalization factor from qlmi @@ -303,15 +310,16 @@ std::vector Steinhardt::normalizeSystem() return system_norms; } -void Steinhardt::aggregatewl(util::ManagedArray& target, - const std::vector>>& source, - const util::ManagedArray& normalization_source) const +void Steinhardt::aggregatewl( + std::shared_ptr>& target, + const std::vector>>>& source, + const std::shared_ptr>& normalization_source) const { util::forLoopWrapper(0, m_Np, [&](size_t begin, size_t end) { for (size_t i = begin; i < end; ++i) { - const auto target_particle_index = target.getIndex({i, 0}); - const auto norm_particle_index = normalization_source.getIndex({i, 0}); + const auto target_particle_index = target->getIndex({i, 0}); + const auto norm_particle_index = normalization_source->getIndex({i, 0}); for (size_t l_index = 0; l_index < m_ls.size(); ++l_index) { const auto l = m_ls[l_index]; @@ -320,13 +328,14 @@ void Steinhardt::aggregatewl(util::ManagedArray& target, const auto normalizationfactor = static_cast(4.0 * M_PI / m_num_ms[l_index]); const auto wigner3j_values = getWigner3j(l); - target[target_particle_index + l_index] - = reduceWigner3j(&(source_l({i, 0})), l, wigner3j_values); + (*target)[target_particle_index + l_index] + = reduceWigner3j(&(*source_l)({i, 0}), l, wigner3j_values); if (m_wl_normalize) { const float normalization = std::sqrt(normalizationfactor) - / normalization_source[norm_particle_index + l_index]; - target[target_particle_index + l_index] *= normalization * normalization * normalization; + / (*normalization_source)[norm_particle_index + l_index]; + (*target)[target_particle_index + l_index] + *= normalization * normalization * normalization; } } } diff --git a/freud/order/Steinhardt.h b/freud/order/Steinhardt.h index 7d6f56fa3..9701259cf 100644 --- a/freud/order/Steinhardt.h +++ b/freud/order/Steinhardt.h @@ -6,6 +6,7 @@ #include #include +#include #include "Box.h" #include "ManagedArray.h" @@ -94,7 +95,7 @@ class Steinhardt } //! Get the last calculated order parameter for each l - const util::ManagedArray& getParticleOrder() const + const std::shared_ptr>& getParticleOrder() const { if (m_wl) { @@ -104,7 +105,7 @@ class Steinhardt } //! Get the last calculated ql for each l - const util::ManagedArray& getQl() const + const std::shared_ptr>& getQl() const { if (m_average) { @@ -114,7 +115,7 @@ class Steinhardt } //! Get the last calculated qlm for each particle and l - const std::vector>>& getQlm() const + const std::vector>>>& getQlm() const { return m_qlmi; } @@ -150,8 +151,9 @@ class Steinhardt } //! Compute the order parameter - void compute(const freud::locality::NeighborList* nlist, const freud::locality::NeighborQuery* points, - freud::locality::QueryArgs qargs); + void compute(const std::shared_ptr& nlist, + const std::shared_ptr& points, + const freud::locality::QueryArgs& qargs); std::vector getL() const { @@ -173,12 +175,14 @@ class Steinhardt void reallocateArrays(unsigned int Np); //! Calculates qlms and the ql order parameter before any further modifications - void baseCompute(const freud::locality::NeighborList* nlist, const freud::locality::NeighborQuery* points, - freud::locality::QueryArgs qargs); + void baseCompute(const std::shared_ptr& nlist, + const std::shared_ptr& points, + const freud::locality::QueryArgs& qargs); //! Calculates the neighbor average ql order parameter - void computeAve(const freud::locality::NeighborList* nlist, const freud::locality::NeighborQuery* points, - freud::locality::QueryArgs qargs); + void computeAve(const std::shared_ptr& nlist, + const std::shared_ptr& points, + const freud::locality::QueryArgs& qargs); //! Compute the system-wide order by averaging over particles, then // reducing over the m values to produce a single scalar. @@ -186,9 +190,9 @@ class Steinhardt //! Sum over Wigner 3j coefficients to compute third-order invariants // wl from second-order invariants ql - void aggregatewl(util::ManagedArray& target, - const std::vector>>& source, - const util::ManagedArray& normalization_source) const; + void aggregatewl(std::shared_ptr>& target, + const std::vector>>>& source, + const std::shared_ptr>& normalization_source) const; // Member variables used for compute unsigned int m_Np {0}; //!< Last number of points computed @@ -201,18 +205,21 @@ class Steinhardt bool m_weighted; //!< Whether to use neighbor weights in computing qlmi (default false) bool m_wl_normalize; //!< Whether to normalize the third-order invariant wl (default false) - std::vector>> m_qlmi; //!< qlm for each particle i - std::vector>> m_qlm; //!< Normalized qlm(Ave) for the whole system + std::vector>>> m_qlmi; //!< qlm for each particle i + std::vector>>> + m_qlm; //!< Normalized qlm(Ave) for the whole system std::vector>> - m_qlm_local; //!< Thread-specific m_qlm(Ave) for each l - util::ManagedArray m_qli; //!< ql locally invariant order parameter for each particle i - util::ManagedArray m_qliAve; //!< Averaged ql with 2nd neighbor shell for each particle i - std::vector>> + m_qlm_local; //!< Thread-specific m_qlm(Ave) for each l + std::shared_ptr> + m_qli; //!< ql locally invariant order parameter for each particle i + std::shared_ptr> + m_qliAve; //!< Averaged ql with 2nd neighbor shell for each particle i + std::vector>>> m_qlmiAve; //!< Averaged qlm with 2nd neighbor shell for each particle i - std::vector>> + std::vector>>> m_qlmAve; //!< Normalized qlmiAve for the whole system std::vector m_norm {0}; //!< System normalized order parameter - util::ManagedArray + std::shared_ptr> m_wli; //!< wl order parameter for each particle i, also used for wl averaged data }; diff --git a/freud/order/Wigner3j.h b/freud/order/Wigner3j.h index 1618a535f..c3b627c2d 100644 --- a/freud/order/Wigner3j.h +++ b/freud/order/Wigner3j.h @@ -7,6 +7,9 @@ #include #include +#include "ManagedArray.h" +#include "VectorMath.h" + /*! \file Wigner3j.h * \brief Stores and reduces over Wigner 3j coefficients for l from 0 to 20 */ diff --git a/freud/order/export-ContinuousCoordination.cc b/freud/order/export-ContinuousCoordination.cc new file mode 100644 index 000000000..9323ac735 --- /dev/null +++ b/freud/order/export-ContinuousCoordination.cc @@ -0,0 +1,35 @@ +// 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 + +#include + +#include "ContinuousCoordination.h" + +namespace freud { namespace order { + +template +using nb_array = nanobind::ndarray; + +namespace detail { + +void export_ContinuousCoordination(nanobind::module_& m) +{ + nanobind::class_(m, "ContinuousCoordination") + .def(nanobind::init, bool, bool>()) + .def("compute", &ContinuousCoordination::compute, nanobind::arg("voronoi")) + .def("getCoordination", &ContinuousCoordination::getCoordination) + .def("getPowers", &ContinuousCoordination::getPowers) + .def("getComputeLog", &ContinuousCoordination::getComputeLog) + .def("getComputeExp", &ContinuousCoordination::getComputeExp) + .def("getNumberOfCoordinations", &ContinuousCoordination::getNumberOfCoordinations); +} + +} // namespace detail + +}; }; // namespace freud::order diff --git a/freud/order/export-Cubatic.cc b/freud/order/export-Cubatic.cc new file mode 100644 index 000000000..8a5dbd0e1 --- /dev/null +++ b/freud/order/export-Cubatic.cc @@ -0,0 +1,57 @@ +// 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 + +#include "Cubatic.h" +#include "VectorMath.h" + +namespace freud { namespace order { + +template +using nb_array = nanobind::ndarray; + +namespace wrap { + +void computeCubatic(const std::shared_ptr& self, + const nb_array>& orientations) +{ + unsigned int const num_orientations = orientations.shape(0); + auto* orientations_data = reinterpret_cast*>(orientations.data()); + + self->compute(orientations_data, num_orientations); +} + +nanobind::tuple getCubaticOrientation(const std::shared_ptr& self) +{ + quat q = self->getCubaticOrientation(); + return nanobind::make_tuple(q.s, q.v.x, q.v.y, q.v.z); +} +}; // namespace wrap + +namespace detail { + +void export_Cubatic(nanobind::module_& m) +{ + nanobind::class_(m, "Cubatic") + .def(nanobind::init()) + .def("compute", &wrap::computeCubatic, nanobind::arg("orientations")) + .def("getTInitial", &Cubatic::getTInitial) + .def("getTFinal", &Cubatic::getTFinal) + .def("getScale", &Cubatic::getScale) + .def("getNReplicates", &Cubatic::getNReplicates) + .def("getSeed", &Cubatic::getSeed) + .def("getCubaticOrderParameter", &Cubatic::getCubaticOrderParameter) + .def("getCubaticOrientation", &wrap::getCubaticOrientation) + .def("getParticleOrderParameter", &Cubatic::getParticleOrderParameter) + .def("getGlobalTensor", &Cubatic::getGlobalTensor) + .def("getCubaticTensor", &Cubatic::getCubaticTensor); +} + +} // namespace detail + +}; }; // namespace freud::order diff --git a/freud/order/export-HexaticTranslational.cc b/freud/order/export-HexaticTranslational.cc new file mode 100644 index 000000000..9244788c7 --- /dev/null +++ b/freud/order/export-HexaticTranslational.cc @@ -0,0 +1,46 @@ +// 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 + +#include "HexaticTranslational.h" +#include "NeighborList.h" +#include "NeighborQuery.h" + +namespace freud { namespace order { + +template +using nb_array = nanobind::ndarray; + +namespace wrap { + +void computeHexaticTranslational(const std::shared_ptr& self, + std::shared_ptr nlist, + std::shared_ptr& points, + const locality::QueryArgs& qargs) +{ + self->compute(std::move(nlist), points, qargs); +} + +}; // namespace wrap + +namespace detail { + +void export_HexaticTranslational(nanobind::module_& m) +{ + nanobind::class_(m, "Hexatic") + .def(nanobind::init()) + .def("compute", &wrap::computeHexaticTranslational, nanobind::arg("nlist").none(), + nanobind::arg("points"), nanobind::arg("qargs")) + .def("getK", &HexaticTranslational::getK) + .def("getOrder", &HexaticTranslational::getOrder) + .def("isWeighted", &HexaticTranslational::isWeighted); +} + +} // namespace detail + +}; }; // namespace freud::order diff --git a/freud/order/export-Nematic.cc b/freud/order/export-Nematic.cc new file mode 100644 index 000000000..4ea8d6bf4 --- /dev/null +++ b/freud/order/export-Nematic.cc @@ -0,0 +1,52 @@ +// 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 "Nematic.h" +#include "VectorMath.h" + +namespace freud { namespace order { + +template +using nb_array = nanobind::ndarray; + +namespace wrap { + +void computeNematic(const std::shared_ptr& self, + const nb_array>& orientations) +{ + unsigned int const num_orientations = orientations.shape(0); + auto* orientations_data = reinterpret_cast*>(orientations.data()); + + self->compute(orientations_data, num_orientations); +} + +nanobind::tuple getNematicDirector(const std::shared_ptr& self) +{ + vec3 nem_d_cpp = self->getNematicDirector(); + return nanobind::make_tuple(nem_d_cpp.x, nem_d_cpp.y, nem_d_cpp.z); +} +}; // namespace wrap + +namespace detail { + +void export_Nematic(nanobind::module_& m) +{ + nanobind::class_(m, "Nematic") + .def(nanobind::init<>()) + .def("compute", &wrap::computeNematic, nanobind::arg("orientations")) + .def("getNematicOrderParameter", &Nematic::getNematicOrderParameter) + .def("getNematicDirector", &wrap::getNematicDirector) + .def("getParticleTensor", &Nematic::getParticleTensor) + .def("getNematicTensor", &Nematic::getNematicTensor); +} + +} // namespace detail + +}; }; // namespace freud::order diff --git a/freud/order/export-RotationalAutocorrelation.cc b/freud/order/export-RotationalAutocorrelation.cc new file mode 100644 index 000000000..1ab4907d4 --- /dev/null +++ b/freud/order/export-RotationalAutocorrelation.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 + +#include "ManagedArray.h" +#include "RotationalAutocorrelation.h" +#include "VectorMath.h" + +namespace freud { namespace order { + +template +using nb_array = nanobind::ndarray; + +namespace wrap { + +void computeRotationalAutocorrelation(const std::shared_ptr& self, + const nb_array>& ref_orientations, + const nb_array>& orientations) +{ + unsigned int const num_orientations = orientations.shape(0); + auto* ref_orientations_data = reinterpret_cast*>(ref_orientations.data()); + auto* orientations_data = reinterpret_cast*>(orientations.data()); + + self->compute(ref_orientations_data, orientations_data, num_orientations); +} + +}; // namespace wrap + +namespace detail { + +void export_RotationalAutocorrelation(nanobind::module_& m) +{ + nanobind::class_(m, "RotationalAutocorrelation") + .def(nanobind::init()) + .def("compute", &wrap::computeRotationalAutocorrelation, nanobind::arg("ref_orientations"), + nanobind::arg("orientations")) + .def("getL", &RotationalAutocorrelation::getL) + .def("getRotationalAutocorrelation", &RotationalAutocorrelation::getRotationalAutocorrelation) + .def("getRAArray", &RotationalAutocorrelation::getRAArray); +} + +} // namespace detail + +}; }; // namespace freud::order diff --git a/freud/order/export-SolidLiquid.cc b/freud/order/export-SolidLiquid.cc new file mode 100644 index 000000000..de6ea7229 --- /dev/null +++ b/freud/order/export-SolidLiquid.cc @@ -0,0 +1,32 @@ +// 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 // NOLINT(misc-include-cleaner): used implicitly +#include + +#include "SolidLiquid.h" + +namespace freud { namespace order { namespace detail { + +void export_SolidLiquid(nanobind::module_& m) +{ + nanobind::class_(m, "SolidLiquid") + .def(nanobind::init()) + .def("compute", &SolidLiquid::compute, nanobind::arg("nlist").none(), nanobind::arg("points"), + nanobind::arg("qargs")) + .def("getL", &SolidLiquid::getL) + .def("getQThreshold", &SolidLiquid::getQThreshold) + .def("getSolidThreshold", &SolidLiquid::getSolidThreshold) + .def("getNormalizeQ", &SolidLiquid::getNormalizeQ) + .def("getClusterIdx", &SolidLiquid::getClusterIdx) + .def("getQlij", &SolidLiquid::getQlij) + .def("getQlm", &SolidLiquid::getQlm) + .def("getClusterSizes", &SolidLiquid::getClusterSizes) + .def("getLargestClusterSize", &SolidLiquid::getLargestClusterSize) + .def("getNList", &SolidLiquid::getNList) + .def("getNumberOfConnections", &SolidLiquid::getNumberOfConnections); +} + +}}; }; // namespace freud::order::detail diff --git a/freud/order/export-Steinhardt.cc b/freud/order/export-Steinhardt.cc new file mode 100644 index 000000000..6b2d5cb46 --- /dev/null +++ b/freud/order/export-Steinhardt.cc @@ -0,0 +1,40 @@ +// 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 + +#include + +#include "Steinhardt.h" + +namespace freud { namespace order { + +template +using nb_array = nanobind::ndarray; + +namespace detail { + +void export_Steinhardt(nanobind::module_& m) +{ + nanobind::class_(m, "Steinhardt") + .def(nanobind::init, bool, bool, bool, bool>()) + .def("compute", &Steinhardt::compute, nanobind::arg("nlist").none(), nanobind::arg("points"), + nanobind::arg("qargs")) + .def("isAverage", &Steinhardt::isAverage) + .def("isWl", &Steinhardt::isWl) + .def("isWeighted", &Steinhardt::isWeighted) + .def("isWlNormalized", &Steinhardt::isWlNormalized) + .def("getL", &Steinhardt::getL) + .def("getOrder", &Steinhardt::getOrder) + .def("getParticleOrder", &Steinhardt::getParticleOrder) + .def("getQlm", &Steinhardt::getQlm) + .def("getQl", &Steinhardt::getQl); +} + +} // namespace detail + +}; }; // namespace freud::order diff --git a/freud/order/module-order.cc b/freud/order/module-order.cc new file mode 100644 index 000000000..eaf3da6c3 --- /dev/null +++ b/freud/order/module-order.cc @@ -0,0 +1,28 @@ +// 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::order::detail { +void export_Nematic(nanobind::module_& m); +void export_RotationalAutocorrelation(nanobind::module_& m); +void export_Steinhardt(nanobind::module_& m); +void export_SolidLiquid(nanobind::module_& m); +void export_ContinuousCoordination(nanobind::module_& m); +void export_Cubatic(nanobind::module_& m); +void export_HexaticTranslational(nanobind::module_& m); +} // namespace freud::order::detail + +using namespace freud::order::detail; + +NB_MODULE(_order, module) // NOLINT(misc-use-anonymous-namespace): caused by nanobind +{ + export_Nematic(module); + export_RotationalAutocorrelation(module); + export_Steinhardt(module); + export_SolidLiquid(module); + export_ContinuousCoordination(module); + export_Cubatic(module); + export_HexaticTranslational(module); +} diff --git a/freud/util/export-ManagedArray.cc b/freud/util/export-ManagedArray.cc index 7e2a03e35..a8b8645a0 100644 --- a/freud/util/export-ManagedArray.cc +++ b/freud/util/export-ManagedArray.cc @@ -1,6 +1,7 @@ // 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 // NOLINT(misc-include-cleaner): used implicitly @@ -14,6 +15,7 @@ void export_ManagedArray(nanobind::module_& module) export_ManagedArray(module, "ManagedArray_double"); export_ManagedArray(module, "ManagedArray_unsignedint"); export_ManagedArray>(module, "ManagedArrayVec3_float"); + export_ManagedArray>(module, "ManagedArray_complexfloat"); }; }; // namespace freud::util::detail