From 004edac2107e36ed1397b5b995ab843e5cb45b89 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 11 Sep 2024 11:28:59 -0400 Subject: [PATCH 01/91] Update order/Nematic --- freud/order/Nematic.cc | 25 ++++++++++++++----------- freud/order/Nematic.h | 16 ++++++++++------ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/freud/order/Nematic.cc b/freud/order/Nematic.cc index cf8473d12..4303792d8 100644 --- a/freud/order/Nematic.cc +++ b/freud/order/Nematic.cc @@ -1,6 +1,9 @@ // 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 "Nematic.h" @@ -17,12 +20,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 +43,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 +71,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..8d80e695c 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,9 @@ 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 From 8ef385ee91388d111dc00db81f6898ef25ac52e7 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 11 Sep 2024 11:29:09 -0400 Subject: [PATCH 02/91] Export nematic and module order --- freud/order/export-Nematic.cc | 49 +++++++++++++++++++++++++++++++++++ freud/order/module-order.cc | 15 +++++++++++ 2 files changed, 64 insertions(+) create mode 100644 freud/order/export-Nematic.cc create mode 100644 freud/order/module-order.cc diff --git a/freud/order/export-Nematic.cc b/freud/order/export-Nematic.cc new file mode 100644 index 000000000..bb08b94e8 --- /dev/null +++ b/freud/order/export-Nematic.cc @@ -0,0 +1,49 @@ +#include +#include +#include +#include // NOLINT(misc-include-cleaner): used implicitly + +#include + +// #include "BondHistogramCompute.h" +// #include "NeighborList.h" +// #include "NeighborQuery.h" +#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); +} + +}; // namespace wrap + + +namespace detail { + +// void export_PMFT(nanobind::module_& m) +// { +// nanobind::class_(m, "PMFT").def("getPCF", &PMFT::getPCF); +// } + +void export_Nematic(nanobind::module_& m) +{ + nanobind::class_(m, "Nematic") + .def(nanobind::init<>()) + .def("compute", &wrap::computeNematic, nanobind::arg("orientations")); +} + +} // namespace detail + +}; }; // end namespace freud::pmft diff --git a/freud/order/module-order.cc b/freud/order/module-order.cc new file mode 100644 index 000000000..b6fc0356b --- /dev/null +++ b/freud/order/module-order.cc @@ -0,0 +1,15 @@ +#include +#include + +namespace freud::order::detail { + // void export_PMFT(nanobind::module_& m); + // void export_PMFTXY(nanobind::module_& m); + void export_Nematic(nanobind::module_& m); +} // namespace freud::pmft::detail + +using namespace freud::order::detail; + +NB_MODULE(_order, module) // NOLINT(misc-use-anonymous-namespace): caused by nanobind +{ + export_Nematic(module); +} From be23b2050f441913a6f6091a42fd206e79ff63b0 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 11 Sep 2024 11:29:16 -0400 Subject: [PATCH 03/91] Update cmakelists --- freud/CMakeLists.txt | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/freud/CMakeLists.txt b/freud/CMakeLists.txt index b84bbb65c..e0a3018da 100644 --- a/freud/CMakeLists.txt +++ b/freud/CMakeLists.txt @@ -89,15 +89,15 @@ add_library( ${VOROPP_SOURCE_DIR}/wall.cc ${VOROPP_SOURCE_DIR}/pre_container.cc ${VOROPP_SOURCE_DIR}/container_prd.cc - # order + 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/Nematic.cc + order/Nematic.h # order/RotationalAutocorrelation.cc # order/RotationalAutocorrelation.h # order/SolidLiquid.cc @@ -170,6 +170,14 @@ 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 +) +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) @@ -196,11 +204,12 @@ set(python_files data.py errors.py locality.py + order.py parallel.py pmft.py plot.py util.py) -# cluster.py density.py diffraction.py environment.py order.py interface.py +# cluster.py density.py diffraction.py environment.py interface.py # msd.py) copy_files_to_build("${python_files}" "freud" "*.py") @@ -213,7 +222,7 @@ if(SKBUILD) # 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) From 82718d867bdd7a5e9a6a3a6563b897904d857c59 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 11 Sep 2024 11:29:25 -0400 Subject: [PATCH 04/91] Move order.pyx to order.py --- freud/{order.pyx => order.py} | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) rename freud/{order.pyx => order.py} (99%) diff --git a/freud/order.pyx b/freud/order.py similarity index 99% rename from freud/order.pyx rename to freud/order.py index 7100d919a..f3828a4f3 100644 --- a/freud/order.pyx +++ b/freud/order.py @@ -10,13 +10,13 @@ Fourier Transforms. """ -from freud.util cimport _Compute, quat, vec3 +from freud.util import _Compute #, quat, vec3 from freud.errors import FreudDeprecationWarning -from cython.operator cimport dereference +# from cython.operator cimport dereference -from freud.locality cimport _PairCompute +# from freud.locality cimport _PairCompute import collections.abc import logging @@ -27,11 +27,11 @@ import freud.locality -cimport numpy as np +import numpy as np -cimport freud._order -cimport freud.locality -cimport freud.util +import freud._order +# cimport freud.locality +# cimport freud.util logger = logging.getLogger(__name__) From cc18bda74984834377f19696cbd75ba208ef0ca8 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 11 Sep 2024 11:32:36 -0400 Subject: [PATCH 05/91] Update init.py --- freud/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freud/__init__.py b/freud/__init__.py index 4e84eb160..32794478c 100644 --- a/freud/__init__.py +++ b/freud/__init__.py @@ -2,8 +2,8 @@ # This file is from the freud project, released under the BSD 3-Clause License. -# cluster,; density,; diffraction,; environment,; interface,; msd,; order, -from . import box, data, locality, parallel, pmft +# cluster,; density,; diffraction,; environment,; interface,; msd,; +from . import box, data, locality, order, parallel, pmft from .box import Box from .locality import AABBQuery, LinkCell, NeighborList from .parallel import NumThreads, get_num_threads, set_num_threads @@ -25,7 +25,7 @@ # "interface", "locality", # "msd", - # "order", + "order", "parallel", "pmft", "Box", From 3aa882d304c1ceb9aba89641856d8bcf3e8b245a Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 11 Sep 2024 13:18:40 -0400 Subject: [PATCH 06/91] Update python side for Nematic order --- freud/order.py | 2034 ++++++++++++++++----------------- freud/order/export-Nematic.cc | 14 +- 2 files changed, 1024 insertions(+), 1024 deletions(-) diff --git a/freud/order.py b/freud/order.py index f3828a4f3..4dbdaab43 100644 --- a/freud/order.py +++ b/freud/order.py @@ -37,129 +37,131 @@ # 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): - r"""Compute the cubatic order parameter :cite:`Haji_Akbari_2015` for a system of - particles using simulated annealing instead of Newton-Raphson root finding. - - Args: - t_initial (float): - Starting temperature. - t_final (float): - Final temperature. - scale (float): - Scaling factor to reduce temperature. - n_replicates (unsigned int, optional): - Number of replicate simulated annealing runs. - (Default value = :code:`1`). - 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): - 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 - - def compute(self, orientations): - r"""Calculates the per-particle and global order parameter. - - Args: - 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) - return self - - @property - def t_initial(self): - """float: The value of the initial temperature.""" - return self.thisptr.getTInitial() - - @property - def t_final(self): - """float: The value of the final temperature.""" - return self.thisptr.getTFinal() - - @property - def scale(self): - """float: The scale.""" - return self.thisptr.getScale() - - @property - def n_replicates(self): - """unsigned int: Number of replicate simulated annealing runs.""" - return self.thisptr.getNReplicates() - - @property - def seed(self): - """unsigned int: Random seed to use in calculations.""" - return self.thisptr.getSeed() - - @_Compute._computed_property - def order(self): - """float: Cubatic order parameter of the system.""" - return self.thisptr.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) - - @_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) - - @_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) - - @_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) - - 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) - - -cdef class Nematic(_Compute): +# np.import_array() + +print("ADFHAFGJAKFHAJ") + +# cdef 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. + +# Args: +# t_initial (float): +# Starting temperature. +# t_final (float): +# Final temperature. +# scale (float): +# Scaling factor to reduce temperature. +# n_replicates (unsigned int, optional): +# Number of replicate simulated annealing runs. +# (Default value = :code:`1`). +# 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): +# 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 + +# def compute(self, orientations): +# r"""Calculates the per-particle and global order parameter. + +# Args: +# 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) +# return self + +# @property +# def t_initial(self): +# """float: The value of the initial temperature.""" +# return self.thisptr.getTInitial() + +# @property +# def t_final(self): +# """float: The value of the final temperature.""" +# return self.thisptr.getTFinal() + +# @property +# def scale(self): +# """float: The scale.""" +# return self.thisptr.getScale() + +# @property +# def n_replicates(self): +# """unsigned int: Number of replicate simulated annealing runs.""" +# return self.thisptr.getNReplicates() + +# @property +# def seed(self): +# """unsigned int: Random seed to use in calculations.""" +# return self.thisptr.getSeed() + +# @_Compute._computed_property +# def order(self): +# """float: Cubatic order parameter of the system.""" +# return self.thisptr.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) + +# @_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) + +# @_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) + +# @_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) + +# 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) + + +class Nematic(_Compute): r"""Compute the nematic order parameter for a system of particles. Note: @@ -183,13 +185,9 @@ def __repr__(self): freud.order.Nematic() """ - cdef freud._order.Nematic *thisptr + def __init__(self): + self._cpp_obj = freud._order.Nematic() - def __cinit__(self): - self.thisptr = new freud._order.Nematic() - - def __dealloc__(self): - del self.thisptr def compute(self, orientations): r"""Calculates the per-particle and global order parameter. @@ -217,920 +215,912 @@ def compute(self, orientations): 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) + 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) + return self._cpp_obj.getParticleTensor() @_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) - - def __repr__(self): - return "freud.order.{cls}()".format(cls=type(self).__name__) - - -cdef 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 - :math:`k = 6`) is analogous to Steinhardt order parameters, and is used to - measure order in the bonds of 2D systems. - - The :math:`k`-atic order parameter for a particle :math:`i` and its - :math:`N_b` neighbors :math:`j` is given by: - - :math:`\psi_k \left( i \right) = \frac{1}{N_b} - \sum \limits_{j=1}^{N_b} e^{i k \phi_{ij}}` - - The parameter :math:`k` governs the symmetry of the order parameter and - typically matches the number of neighbors to be found for each particle. - The quantity :math:`\phi_{ij}` is the angle between the - vector :math:`r_{ij}` and :math:`\left(1, 0\right)`. - - If the weighted mode is enabled, contributions of each neighbor are - weighted. Neighbor weights :math:`w_{ij}` default to 1 but are defined for a - :class:`freud.locality.NeighborList` from :class:`freud.locality.Voronoi` - or one with user-provided weights. The formula is modified as follows: - - :math:`\psi'_k \left( i \right) = \frac{1}{\sum_{j=1}^{N_b} w_{ij}} - \sum \limits_{j=1}^{N_b} w_{ij} e^{i k \phi_{ij}}` - - The hexatic order parameter as written above is **complex-valued**. The - **magnitude** of the complex value, - :code:`np.abs(hex_order.particle_order)`, is frequently what is desired - when determining the :math:`k`-atic order for each particle. The complex - phase angle :code:`np.angle(hex_order.particle_order)` indicates the - orientation of the bonds as an angle measured counterclockwise from the - vector :math:`\left(1, 0\right)`. The complex valued order parameter is - not rotationally invariant because of this phase angle, but the magnitude - *is* rotationally invariant. - - .. note:: - **2D:** :class:`freud.order.Hexatic` is only defined for 2D systems. - The points must be passed in as :code:`[x, y, 0]`. - - Args: - k (unsigned int, optional): - Symmetry of order parameter (Default value = :code:`6`). - weighted (bool, optional): - Determines whether to use neighbor weights in the computation of - spherical harmonics over neighbors. If enabled and used with a - 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 compute(self, system, neighbors=None): - r"""Calculates the hexatic order parameter. - - Example:: - - >>> box, points = freud.data.make_random_system( - ... box_size=10, num_points=100, is2D=True, seed=0) - >>> # Compute the hexatic (6-fold) order for the 2D system - >>> hex_order = freud.order.Hexatic(k=6) - >>> hex_order.compute(system=(box, points)) - freud.order.Hexatic(...) - >>> print(hex_order.particle_order) - [...] - - Args: - system: - Any object that is a valid argument to - :class:`freud.locality.NeighborQuery.from_system`. - neighbors (:class:`freud.locality.NeighborList` or dict, optional): - Either a :class:`NeighborList ` of - neighbor pairs to use in the calculation, or a dictionary of - `query arguments - `_ - (Default value: None). - """ # noqa: E501 - cdef: - freud.locality.NeighborQuery nq - freud.locality.NeighborList nlist - freud.locality._QueryArgs qargs - const float[:, ::1] l_query_points - unsigned int num_query_points - - nq, nlist, qargs, l_query_points, num_query_points = \ - self._preprocess_arguments(system, neighbors=neighbors) - self.thisptr.compute(nlist.get_ptr(), - nq.get_ptr(), dereference(qargs.thisptr)) - return self - - @property - def default_query_args(self): - """The default query arguments are - :code:`{'mode': 'nearest', 'num_neighbors': self.k}`.""" - return dict(mode="nearest", num_neighbors=self.k) - - @_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.getOrder(), - freud.util.arr_type_t.COMPLEX_FLOAT) - - @property - def k(self): - """unsigned int: Symmetry of the order parameter.""" - return self.thisptr.getK() - - @property - def weighted(self): - """bool: Whether neighbor weights were used in the computation.""" - return self.thisptr.isWeighted() - - def __repr__(self): - return "freud.order.{cls}(k={k}, weighted={weighted})".format( - cls=type(self).__name__, k=self.k, weighted=self.weighted) - - def plot(self, ax=None): - """Plot order parameter distribution. - - Args: - ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If - :code:`None`, make a new figure and axis - (Default value = :code:`None`). - - Returns: - (: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) - - return freud.plot.histogram_plot( - np.absolute(self.particle_order), - title="Hexatic Order Parameter " + xlabel, - xlabel=xlabel, - ylabel=r"Number of particles", - 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): - 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`. - - Implements the local rotationally invariant :math:`q_l` or :math:`w_l` - order parameter described by Steinhardt. - - First, we describe the computation of :math:`q_l(i)`. For a particle :math:`i`, - we calculate the quantity :math:`q_{lm}` by summing the spherical harmonics - between particle :math:`i` and its neighbors :math:`j` in a local region: - - .. math:: - - q_{lm}(i) = \frac{1}{N_b} \sum \limits_{j=1}^{N_b} - Y_{lm}(\theta(\vec{r}_{ij}), \phi(\vec{r}_{ij})) - - Then the :math:`q_l` order parameter is computed by combining the :math:`q_{lm}` - in a rotationally invariant fashion to remove local orientational order: - - .. math:: - - q_l(i) = \sqrt{\frac{4\pi}{2l+1} \sum \limits_{m=-l}^{l} - |q_{lm}(i)|^2 } - - If the ``wl`` parameter is ``True``, this class computes the quantity - :math:`w_l`, defined as a weighted average over the - :math:`q_{lm}(i)` values using `Wigner 3-j symbols - `__ (related to `Clebsch-Gordan - coefficients - `__). - The resulting combination is rotationally invariant: - - .. math:: - - w_l(i) = \sum \limits_{m_1 + m_2 + m_3 = 0} \begin{pmatrix} - l & l & l \\ - m_1 & m_2 & m_3 - \end{pmatrix} - q_{lm_1}(i) q_{lm_2}(i) q_{lm_3}(i) - - If ``wl`` is ``True``, then setting the ``wl_normalize`` parameter to ``True`` will - normalize the :math:`w_l` order parameter as follows (if ``wl=False``, - ``wl_normalize`` has no effect): - - .. math:: - - w_l(i) = \frac{ - \sum \limits_{m_1 + m_2 + m_3 = 0} \begin{pmatrix} - l & l & l \\ - m_1 & m_2 & m_3 - \end{pmatrix} - q_{lm_1}(i) q_{lm_2}(i) q_{lm_3}(i)} - {\left(\sum \limits_{m=-l}^{l} |q_{lm}(i)|^2 \right)^{3/2}} - - If ``average`` is ``True``, the class computes a variant of this order - parameter that performs an average over the first and second shell combined - :cite:`Lechner_2008`. To compute this parameter, we perform a second - averaging over the first neighbor shell of the particle to implicitly - include information about the second neighbor shell. This averaging is - performed by replacing the value :math:`q_{lm}(i)` in the original - definition by :math:`\overline{q}_{lm}(i)`, the average value of - :math:`q_{lm}(k)` over all the :math:`N_b` neighbors :math:`k` - of particle :math:`i`, including particle :math:`i` itself: - - .. math:: - \overline{q}_{lm}(i) = \frac{1}{N_b} \sum \limits_{k=0}^{N_b} - q_{lm}(k) - - If ``weighted`` is True, the contributions of each neighbor are weighted. - Neighbor weights :math:`w_{ij}` are defined for a - :class:`freud.locality.NeighborList` obtained from - :class:`freud.locality.Voronoi` or one with user-provided weights, and - default to 1 if not otherwise provided. The formulas are modified as - follows, replacing :math:`q_{lm}(i)` with the weighted value - :math:`q'_{lm}(i)`: - - .. math:: - - q'_{lm}(i) = \frac{1}{\sum_{j=1}^{N_b} w_{ij}} - \sum \limits_{j=1}^{N_b} w_{ij} Y_{lm}(\theta(\vec{r}_{ij}), - \phi(\vec{r}_{ij})) - - .. note:: - The value of per-particle order parameter will be set to NaN for - particles with no neighbors. We choose this value rather than setting - the order parameter to 0 because in more complex order parameter - calculations (such as when computing the :math:`w_l`), it is possible - to observe a value of 0 for the per-particle order parameter even with - a finite number of neighbors. If you would like to ignore this - distinction, you can mask the output order parameter values using - NumPy: :code:`numpy.nan_to_num(particle_order)`. - - Args: - l (unsigned int or sequence of unsigned int): - One or more spherical harmonic quantum number l's used to compute - the Steinhardt order parameter. - average (bool, optional): - Determines whether to calculate the averaged Steinhardt order - parameter (Default value = :code:`False`). - wl (bool, optional): - Determines whether to use the :math:`w_l` version of the Steinhardt - order parameter (Default value = :code:`False`). - weighted (bool, optional): - Determines whether to use neighbor weights in the computation of - spherical harmonics over neighbors. If enabled and used with a - Voronoi neighbor list, this results in the 3D Minkowski Structure - Metrics :math:`q'_l` :cite:`Mickel2013` (Default value = - :code:`False`). - 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): - 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 - - @property - def average(self): - """bool: Whether the averaged Steinhardt order parameter was - calculated.""" - return self.thisptr.isAverage() - - @property - def wl(self): - """bool: Whether the :math:`w_l` version of the Steinhardt order - parameter was used.""" - return self.thisptr.isWl() - - @property - def weighted(self): - """bool: Whether neighbor weights were used in the computation.""" - return self.thisptr.isWeighted() - - @property - def wl_normalize(self): - return self.thisptr.isWlNormalized() - - @property - def l(self): # noqa: E743 - """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()) - return ls[0] if len(ls) == 1 else ls - - @_Compute._computed_property - def order(self): - r"""float: The system wide normalization of the order parameter, - computed by averaging the :math:`q_{lm}` values (or - :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()) - return order[0] if len(order) == 1 else order - - @_Compute._computed_property - 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 - - @_Compute._computed_property - def ql(self): - """:math:`\\left(N_{particles}, N_l\\right)` :class:`numpy.ndarray`: - :math:`q_l` Steinhardt order parameter for each particle (filled with - :code:`nan` for particles with no neighbors). This is always available, - 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 - - @_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())] - return qlm_list if len(qlm_list) > 1 else qlm_list[0] - - def compute(self, system, neighbors=None): - r"""Compute the order parameter. - - Example:: - - >>> box, points = freud.data.make_random_system(10, 100, seed=0) - >>> ql = freud.order.Steinhardt(l=6) - >>> ql.compute((box, points), {'r_max':3}) - freud.order.Steinhardt(l=6, ...) - - Args: - system: - Any object that is a valid argument to - :class:`freud.locality.NeighborQuery.from_system`. - neighbors (:class:`freud.locality.NeighborList` or dict, optional): - Either a :class:`NeighborList ` of - neighbor pairs to use in the calculation, or a dictionary of - `query arguments - `_ - (Default value: None). - """ # noqa: E501 - cdef: - freud.locality.NeighborQuery nq - freud.locality.NeighborList nlist - freud.locality._QueryArgs qargs - const float[:, ::1] l_query_points - unsigned int num_query_points - - nq, nlist, qargs, l_query_points, num_query_points = \ - self._preprocess_arguments(system, neighbors=neighbors) - - self.thisptr.compute(nlist.get_ptr(), - nq.get_ptr(), - dereference(qargs.thisptr)) - 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) - - def plot(self, ax=None): - """Plot order parameter distribution. - - Args: - ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If - :code:`None`, make a new figure and axis - (Default value = :code:`None`). - - Returns: - (:class:`matplotlib.axes.Axes`): Axis with the plot. - """ - import freud.plot - - ls = self.l - if not isinstance(ls, list): - ls = [ls] - - 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 '') - for sph_l in ls - ] - xlabel = ', '.join(legend_labels) - - # Don't print legend if only one l requested. - if len(legend_labels) == 1: - legend_labels = None - - return freud.plot.histogram_plot( - self.particle_order, - title="Steinhardt Order Parameter " + xlabel, - xlabel=xlabel, - ylabel=r"Number of particles", - ax=ax, - 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): - r"""Identifies solid-like clusters using dot products of :math:`q_{lm}`. - - The solid-liquid order parameter :cite:`Wolde:1995aa,Filion_2010` uses a - Steinhardt-like approach to identify solid-like particles. First, a bond - parameter :math:`q_l(i, j)` is computed for each neighbor bond. - - If :code:`normalize_q` is true (default), the bond parameter is given by - :math:`q_l(i, j) = \frac{\sum \limits_{m=-l}^{l} \text{Re}~q_{lm}(i) q_{lm}^*(j)} - {\sqrt{\sum \limits_{m=-l}^{l} \lvert q_{lm}(i) \rvert^2} - \sqrt{\sum \limits_{m=-l}^{l} \lvert q_{lm}(j) \rvert^2}}` - - If :code:`normalize_q` is false, then the denominator of the above - expression is left out. - - Next, the bonds are filtered to keep only "solid-like" bonds with - :math:`q_l(i, j)` above a cutoff value :math:`q_{threshold}`. - - If a particle has more than :math:`S_{threshold}` solid-like bonds, then - the particle is considered solid-like. Finally, solid-like particles are - clustered. - - Args: - l (unsigned int): - Spherical harmonic quantum number l. - q_threshold (float): - Value of dot product threshold when evaluating - :math:`q_l(i, j)` to determine if a bond is solid-like. For - :math:`l=6`, 0.7 is generally good for FCC or BCC structures - :cite:`Filion_2010`. - solid_threshold (unsigned int): - Minimum required number of adjacent solid-like bonds for a particle - to be considered solid-like for clustering. For :math:`l=6`, 6-8 - is generally good for FCC or BCC structures. - 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 compute(self, system, neighbors=None): - r"""Compute the order parameter. - - Args: - system: - Any object that is a valid argument to - :class:`freud.locality.NeighborQuery.from_system`. - neighbors (:class:`freud.locality.NeighborList` or dict, optional): - Either a :class:`NeighborList ` of - neighbor pairs to use in the calculation, or a dictionary of - `query arguments - `_ - (Default value: None). - """ - 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)) - return self - - @property - def l(self): # noqa: E743 - """unsigned int: Spherical harmonic quantum number l.""" - return self.thisptr.getL() - - @property - def q_threshold(self): - """float: Value of dot product threshold.""" - return self.thisptr.getQThreshold() - - @property - def solid_threshold(self): - """float: Value of number-of-bonds threshold.""" - return self.thisptr.getSolidThreshold() - - @property - def normalize_q(self): - """bool: Whether the dot product is normalized.""" - return self.thisptr.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) - - @_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) - - @_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) - - @_Compute._computed_property - def cluster_sizes(self): - """:math:`(N_{clusters}, )` :class:`np.ndarray`: The sizes of all - clusters.""" - return np.asarray(self.thisptr.getClusterSizes()) - - @_Compute._computed_property - def largest_cluster_size(self): - """unsigned int: The largest cluster size.""" - return self.thisptr.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 - - @_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) - - 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) - - def plot(self, ax=None): - """Plot solid-like cluster distribution. - - Args: - ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If - :code:`None`, make a new figure and axis - (Default value = :code:`None`). - - Returns: - (: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) - - 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): - """Calculates a measure of total rotational autocorrelation. - - For any calculation of rotational correlations of extended (i.e. non-point) - particles, encoding the symmetries of these particles is crucial to - appropriately determining correlations. For systems of anisotropic - particles in three dimensions, representing such equivalence can be quite - mathematically challenging. This calculation is based on the hyperspherical - harmonics as laid out in :cite:`Karas2019`. Generalizations of spherical - harmonics to four dimensions, hyperspherical harmonics provide a natural - basis for periodic functions in 4 dimensions just as harmonic functions - (sines and cosines) or spherical harmonics do in lower dimensions. The idea - behind this calculation is to embed orientation quaternions into a - 4-dimensional space and then use hyperspherical harmonics to find - correlations in a symmetry-aware fashion. - - The choice of the hyperspherical harmonic parameter :math:`l` determines - the symmetry of the functions. The output is not a correlation function, - but rather a scalar value that measures total system orientational - correlation with an initial state. As such, the output can be treated as an - order parameter measuring degrees of rotational (de)correlation. For - analysis of a trajectory, the compute call needs to be - done at each trajectory frame. - - Args: - l (int): - Order of the hyperspherical harmonic. Must be a positive, even - integer. - """ - cdef freud._order.RotationalAutocorrelation * thisptr - - def __cinit__(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 - - def compute(self, ref_orientations, orientations): - """Calculates the rotational autocorrelation function for a single frame. - - Args: - ref_orientations ((:math:`N_{orientations}`, 4) :class:`numpy.ndarray`): - 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) - return self - - @_Compute._computed_property - def order(self): - """float: Autocorrelation of the system.""" - return self.thisptr.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) - - @property - def l(self): # noqa: E743 - """int: The azimuthal quantum number, which defines the order of the - hyperspherical harmonic.""" - return self.thisptr.getL() - - def __repr__(self): - return "freud.order.{cls}(l={sph_l})".format(cls=type(self).__name__, - sph_l=self.l) - - -cdef class ContinuousCoordination(_PairCompute): - r"""Computes the continuous local coordination number. - - The :class:`ContinuousCoordination` class implements extensions of the Voronoi - discrete coordination number to the real numbers. The formulas for the - various implementations are: - - Power: - - .. math:: - - CN_p = N^{2.0 - m} \sum_{i=1}^{k} - {\left[\left(\frac{V_i}{V}\right)^{m}\right]}^{-1} - - Log: - - .. math:: - - CN_{log} = \frac{-1}{\log{N}} \sum_{i=1}^{k}\log{\left(\frac{V_i}{V}\right)} - - Exponential: - - .. math:: - - CN_{exp} = \sum_{i=1}^{k}\exp{\left(\frac{V_i}{V} - \frac{1}{N} \right)} - - where :math:`k` is the number of neighbors a particle has, :math:`V_i` is - the volume of the pyramid (or area of the triangle in 2D) whose base is the - Voronoi polytope facet between the central particle and neighbor :math:`i` - and whose height is half the distance vector, and :math:`V` is the - volume/area of the Voronoi polytope. - - Note: - When using multiple powers, space them out to avoid high correlations. A - minimum spacing of 2.0 is recommended with even larger values leading to - less correlation. - - Args: - powers (list[float], optional): The powers to compute the continuous - coordination number for. The default value indicates only compute - for power 2.0. - (Default value: None) - compute_log (`bool`, optional): Whether to compute the log continuous - coordination number. - (Default value: :code:`True`) - compute_exp (`bool`, optional): Whether to compute the exp continuous - coordination number. - (Default value: :code:`True`) - """ - cdef freud._order.ContinuousCoordination* thisptr - - def __cinit__(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 - - def compute(self, system=None, voronoi=None): - r"""Calculates the local coordination number for the specified points. - - Example:: - - >>> import freud - >>> box, points = freud.data.make_random_system(10, 100, seed=0) - >>> # Compute ContinuousCoordination - >>> coord = freud.order.ContinuousCoordination([2, 4], True) - >>> coord.compute(system=(box, points)) - freud.order.ContinuousCoordination(...) - - Args: - system (optional): - Any object that is a valid argument to - :class:`freud.locality.NeighborQuery.from_system`. - (Default value: None). - voronoi (:class:`freud.locality.Voronoi`, optional): - A precomputed Voronoi compute object. If provided, the object is - 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.") - 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.") - cpp_voronoi = voronoi - self.thisptr.compute(cpp_voronoi.thisptr) - return self - - @_Compute._computed_property - def particle_order(self): - """(:math:`(N_{points}, N_{coord}`) :class:`numpy.ndarray`: \ - coordination of points per query point. - - Coordination numbers are in order of selected powers, log, and exp. - """ - return self.coordination - - @_Compute._computed_property - def coordination(self): - """(:math:`(N_{points}, N_{coord}`) :class:`numpy.ndarray`): \ - coordination of points per query point. - - 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) - - @property - def powers(self): - """list[float]: The powers to compute the continuous coordination number. - - Changes to this property are not reflected when computing coordination - numbers. - """ - return self.thisptr.getPowers() - - @property - def compute_log(self): - """bool: Whether to compute the log continuous coordination number.""" - return self.thisptr.getComputeLog() - - @property - def compute_exp(self): - """bool: Whether to compute the exponential coordination number.""" - return self.thisptr.getComputeExp() - - @property - def number_of_coordinations(self): - """int: The number of coordination numbers computed.""" - return self.thisptr.getNumberOfCoordinations() + return self._cpp_obj.getNematicTensor() 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, - ) + return f"freud.order.{type(self).__name__}()" + + +# cdef 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 +# :math:`k = 6`) is analogous to Steinhardt order parameters, and is used to +# measure order in the bonds of 2D systems. + +# The :math:`k`-atic order parameter for a particle :math:`i` and its +# :math:`N_b` neighbors :math:`j` is given by: + +# :math:`\psi_k \left( i \right) = \frac{1}{N_b} +# \sum \limits_{j=1}^{N_b} e^{i k \phi_{ij}}` + +# The parameter :math:`k` governs the symmetry of the order parameter and +# typically matches the number of neighbors to be found for each particle. +# The quantity :math:`\phi_{ij}` is the angle between the +# vector :math:`r_{ij}` and :math:`\left(1, 0\right)`. + +# If the weighted mode is enabled, contributions of each neighbor are +# weighted. Neighbor weights :math:`w_{ij}` default to 1 but are defined for a +# :class:`freud.locality.NeighborList` from :class:`freud.locality.Voronoi` +# or one with user-provided weights. The formula is modified as follows: + +# :math:`\psi'_k \left( i \right) = \frac{1}{\sum_{j=1}^{N_b} w_{ij}} +# \sum \limits_{j=1}^{N_b} w_{ij} e^{i k \phi_{ij}}` + +# The hexatic order parameter as written above is **complex-valued**. The +# **magnitude** of the complex value, +# :code:`np.abs(hex_order.particle_order)`, is frequently what is desired +# when determining the :math:`k`-atic order for each particle. The complex +# phase angle :code:`np.angle(hex_order.particle_order)` indicates the +# orientation of the bonds as an angle measured counterclockwise from the +# vector :math:`\left(1, 0\right)`. The complex valued order parameter is +# not rotationally invariant because of this phase angle, but the magnitude +# *is* rotationally invariant. + +# .. note:: +# **2D:** :class:`freud.order.Hexatic` is only defined for 2D systems. +# The points must be passed in as :code:`[x, y, 0]`. + +# Args: +# k (unsigned int, optional): +# Symmetry of order parameter (Default value = :code:`6`). +# weighted (bool, optional): +# Determines whether to use neighbor weights in the computation of +# spherical harmonics over neighbors. If enabled and used with a +# 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 compute(self, system, neighbors=None): +# r"""Calculates the hexatic order parameter. + +# Example:: + +# >>> box, points = freud.data.make_random_system( +# ... box_size=10, num_points=100, is2D=True, seed=0) +# >>> # Compute the hexatic (6-fold) order for the 2D system +# >>> hex_order = freud.order.Hexatic(k=6) +# >>> hex_order.compute(system=(box, points)) +# freud.order.Hexatic(...) +# >>> print(hex_order.particle_order) +# [...] + +# Args: +# system: +# Any object that is a valid argument to +# :class:`freud.locality.NeighborQuery.from_system`. +# neighbors (:class:`freud.locality.NeighborList` or dict, optional): +# Either a :class:`NeighborList ` of +# neighbor pairs to use in the calculation, or a dictionary of +# `query arguments +# `_ +# (Default value: None). +# """ # noqa: E501 +# cdef: +# freud.locality.NeighborQuery nq +# freud.locality.NeighborList nlist +# freud.locality._QueryArgs qargs +# const float[:, ::1] l_query_points +# unsigned int num_query_points + +# nq, nlist, qargs, l_query_points, num_query_points = \ +# self._preprocess_arguments(system, neighbors=neighbors) +# self.thisptr.compute(nlist.get_ptr(), +# nq.get_ptr(), dereference(qargs.thisptr)) +# return self + +# @property +# def default_query_args(self): +# """The default query arguments are +# :code:`{'mode': 'nearest', 'num_neighbors': self.k}`.""" +# return dict(mode="nearest", num_neighbors=self.k) + +# @_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.getOrder(), +# freud.util.arr_type_t.COMPLEX_FLOAT) + +# @property +# def k(self): +# """unsigned int: Symmetry of the order parameter.""" +# return self.thisptr.getK() + +# @property +# def weighted(self): +# """bool: Whether neighbor weights were used in the computation.""" +# return self.thisptr.isWeighted() + +# def __repr__(self): +# return "freud.order.{cls}(k={k}, weighted={weighted})".format( +# cls=type(self).__name__, k=self.k, weighted=self.weighted) + +# def plot(self, ax=None): +# """Plot order parameter distribution. + +# Args: +# ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If +# :code:`None`, make a new figure and axis +# (Default value = :code:`None`). + +# Returns: +# (: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) + +# return freud.plot.histogram_plot( +# np.absolute(self.particle_order), +# title="Hexatic Order Parameter " + xlabel, +# xlabel=xlabel, +# ylabel=r"Number of particles", +# 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): +# 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`. + +# Implements the local rotationally invariant :math:`q_l` or :math:`w_l` +# order parameter described by Steinhardt. + +# First, we describe the computation of :math:`q_l(i)`. For a particle :math:`i`, +# we calculate the quantity :math:`q_{lm}` by summing the spherical harmonics +# between particle :math:`i` and its neighbors :math:`j` in a local region: + +# .. math:: + +# q_{lm}(i) = \frac{1}{N_b} \sum \limits_{j=1}^{N_b} +# Y_{lm}(\theta(\vec{r}_{ij}), \phi(\vec{r}_{ij})) + +# Then the :math:`q_l` order parameter is computed by combining the :math:`q_{lm}` +# in a rotationally invariant fashion to remove local orientational order: + +# .. math:: + +# q_l(i) = \sqrt{\frac{4\pi}{2l+1} \sum \limits_{m=-l}^{l} +# |q_{lm}(i)|^2 } + +# If the ``wl`` parameter is ``True``, this class computes the quantity +# :math:`w_l`, defined as a weighted average over the +# :math:`q_{lm}(i)` values using `Wigner 3-j symbols +# `__ (related to `Clebsch-Gordan +# coefficients +# `__). +# The resulting combination is rotationally invariant: + +# .. math:: + +# w_l(i) = \sum \limits_{m_1 + m_2 + m_3 = 0} \begin{pmatrix} +# l & l & l \\ +# m_1 & m_2 & m_3 +# \end{pmatrix} +# q_{lm_1}(i) q_{lm_2}(i) q_{lm_3}(i) + +# If ``wl`` is ``True``, then setting the ``wl_normalize`` parameter to ``True`` will +# normalize the :math:`w_l` order parameter as follows (if ``wl=False``, +# ``wl_normalize`` has no effect): + +# .. math:: + +# w_l(i) = \frac{ +# \sum \limits_{m_1 + m_2 + m_3 = 0} \begin{pmatrix} +# l & l & l \\ +# m_1 & m_2 & m_3 +# \end{pmatrix} +# q_{lm_1}(i) q_{lm_2}(i) q_{lm_3}(i)} +# {\left(\sum \limits_{m=-l}^{l} |q_{lm}(i)|^2 \right)^{3/2}} + +# If ``average`` is ``True``, the class computes a variant of this order +# parameter that performs an average over the first and second shell combined +# :cite:`Lechner_2008`. To compute this parameter, we perform a second +# averaging over the first neighbor shell of the particle to implicitly +# include information about the second neighbor shell. This averaging is +# performed by replacing the value :math:`q_{lm}(i)` in the original +# definition by :math:`\overline{q}_{lm}(i)`, the average value of +# :math:`q_{lm}(k)` over all the :math:`N_b` neighbors :math:`k` +# of particle :math:`i`, including particle :math:`i` itself: + +# .. math:: +# \overline{q}_{lm}(i) = \frac{1}{N_b} \sum \limits_{k=0}^{N_b} +# q_{lm}(k) + +# If ``weighted`` is True, the contributions of each neighbor are weighted. +# Neighbor weights :math:`w_{ij}` are defined for a +# :class:`freud.locality.NeighborList` obtained from +# :class:`freud.locality.Voronoi` or one with user-provided weights, and +# default to 1 if not otherwise provided. The formulas are modified as +# follows, replacing :math:`q_{lm}(i)` with the weighted value +# :math:`q'_{lm}(i)`: + +# .. math:: + +# q'_{lm}(i) = \frac{1}{\sum_{j=1}^{N_b} w_{ij}} +# \sum \limits_{j=1}^{N_b} w_{ij} Y_{lm}(\theta(\vec{r}_{ij}), +# \phi(\vec{r}_{ij})) + +# .. note:: +# The value of per-particle order parameter will be set to NaN for +# particles with no neighbors. We choose this value rather than setting +# the order parameter to 0 because in more complex order parameter +# calculations (such as when computing the :math:`w_l`), it is possible +# to observe a value of 0 for the per-particle order parameter even with +# a finite number of neighbors. If you would like to ignore this +# distinction, you can mask the output order parameter values using +# NumPy: :code:`numpy.nan_to_num(particle_order)`. + +# Args: +# l (unsigned int or sequence of unsigned int): +# One or more spherical harmonic quantum number l's used to compute +# the Steinhardt order parameter. +# average (bool, optional): +# Determines whether to calculate the averaged Steinhardt order +# parameter (Default value = :code:`False`). +# wl (bool, optional): +# Determines whether to use the :math:`w_l` version of the Steinhardt +# order parameter (Default value = :code:`False`). +# weighted (bool, optional): +# Determines whether to use neighbor weights in the computation of +# spherical harmonics over neighbors. If enabled and used with a +# Voronoi neighbor list, this results in the 3D Minkowski Structure +# Metrics :math:`q'_l` :cite:`Mickel2013` (Default value = +# :code:`False`). +# 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): +# 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 + +# @property +# def average(self): +# """bool: Whether the averaged Steinhardt order parameter was +# calculated.""" +# return self.thisptr.isAverage() + +# @property +# def wl(self): +# """bool: Whether the :math:`w_l` version of the Steinhardt order +# parameter was used.""" +# return self.thisptr.isWl() + +# @property +# def weighted(self): +# """bool: Whether neighbor weights were used in the computation.""" +# return self.thisptr.isWeighted() + +# @property +# def wl_normalize(self): +# return self.thisptr.isWlNormalized() + +# @property +# def l(self): # noqa: E743 +# """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()) +# return ls[0] if len(ls) == 1 else ls + +# @_Compute._computed_property +# def order(self): +# r"""float: The system wide normalization of the order parameter, +# computed by averaging the :math:`q_{lm}` values (or +# :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()) +# return order[0] if len(order) == 1 else order + +# @_Compute._computed_property +# 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 + +# @_Compute._computed_property +# def ql(self): +# """:math:`\\left(N_{particles}, N_l\\right)` :class:`numpy.ndarray`: +# :math:`q_l` Steinhardt order parameter for each particle (filled with +# :code:`nan` for particles with no neighbors). This is always available, +# 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 + +# @_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())] +# return qlm_list if len(qlm_list) > 1 else qlm_list[0] + +# def compute(self, system, neighbors=None): +# r"""Compute the order parameter. + +# Example:: + +# >>> box, points = freud.data.make_random_system(10, 100, seed=0) +# >>> ql = freud.order.Steinhardt(l=6) +# >>> ql.compute((box, points), {'r_max':3}) +# freud.order.Steinhardt(l=6, ...) + +# Args: +# system: +# Any object that is a valid argument to +# :class:`freud.locality.NeighborQuery.from_system`. +# neighbors (:class:`freud.locality.NeighborList` or dict, optional): +# Either a :class:`NeighborList ` of +# neighbor pairs to use in the calculation, or a dictionary of +# `query arguments +# `_ +# (Default value: None). +# """ # noqa: E501 +# cdef: +# freud.locality.NeighborQuery nq +# freud.locality.NeighborList nlist +# freud.locality._QueryArgs qargs +# const float[:, ::1] l_query_points +# unsigned int num_query_points + +# nq, nlist, qargs, l_query_points, num_query_points = \ +# self._preprocess_arguments(system, neighbors=neighbors) + +# self.thisptr.compute(nlist.get_ptr(), +# nq.get_ptr(), +# dereference(qargs.thisptr)) +# 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) + +# def plot(self, ax=None): +# """Plot order parameter distribution. + +# Args: +# ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If +# :code:`None`, make a new figure and axis +# (Default value = :code:`None`). + +# Returns: +# (:class:`matplotlib.axes.Axes`): Axis with the plot. +# """ +# import freud.plot + +# ls = self.l +# if not isinstance(ls, list): +# ls = [ls] + +# 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 '') +# for sph_l in ls +# ] +# xlabel = ', '.join(legend_labels) + +# # Don't print legend if only one l requested. +# if len(legend_labels) == 1: +# legend_labels = None + +# return freud.plot.histogram_plot( +# self.particle_order, +# title="Steinhardt Order Parameter " + xlabel, +# xlabel=xlabel, +# ylabel=r"Number of particles", +# ax=ax, +# 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): +# r"""Identifies solid-like clusters using dot products of :math:`q_{lm}`. + +# The solid-liquid order parameter :cite:`Wolde:1995aa,Filion_2010` uses a +# Steinhardt-like approach to identify solid-like particles. First, a bond +# parameter :math:`q_l(i, j)` is computed for each neighbor bond. + +# If :code:`normalize_q` is true (default), the bond parameter is given by +# :math:`q_l(i, j) = \frac{\sum \limits_{m=-l}^{l} \text{Re}~q_{lm}(i) q_{lm}^*(j)} +# {\sqrt{\sum \limits_{m=-l}^{l} \lvert q_{lm}(i) \rvert^2} +# \sqrt{\sum \limits_{m=-l}^{l} \lvert q_{lm}(j) \rvert^2}}` + +# If :code:`normalize_q` is false, then the denominator of the above +# expression is left out. + +# Next, the bonds are filtered to keep only "solid-like" bonds with +# :math:`q_l(i, j)` above a cutoff value :math:`q_{threshold}`. + +# If a particle has more than :math:`S_{threshold}` solid-like bonds, then +# the particle is considered solid-like. Finally, solid-like particles are +# clustered. + +# Args: +# l (unsigned int): +# Spherical harmonic quantum number l. +# q_threshold (float): +# Value of dot product threshold when evaluating +# :math:`q_l(i, j)` to determine if a bond is solid-like. For +# :math:`l=6`, 0.7 is generally good for FCC or BCC structures +# :cite:`Filion_2010`. +# solid_threshold (unsigned int): +# Minimum required number of adjacent solid-like bonds for a particle +# to be considered solid-like for clustering. For :math:`l=6`, 6-8 +# is generally good for FCC or BCC structures. +# 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 compute(self, system, neighbors=None): +# r"""Compute the order parameter. + +# Args: +# system: +# Any object that is a valid argument to +# :class:`freud.locality.NeighborQuery.from_system`. +# neighbors (:class:`freud.locality.NeighborList` or dict, optional): +# Either a :class:`NeighborList ` of +# neighbor pairs to use in the calculation, or a dictionary of +# `query arguments +# `_ +# (Default value: None). +# """ +# 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)) +# return self + +# @property +# def l(self): # noqa: E743 +# """unsigned int: Spherical harmonic quantum number l.""" +# return self.thisptr.getL() + +# @property +# def q_threshold(self): +# """float: Value of dot product threshold.""" +# return self.thisptr.getQThreshold() + +# @property +# def solid_threshold(self): +# """float: Value of number-of-bonds threshold.""" +# return self.thisptr.getSolidThreshold() + +# @property +# def normalize_q(self): +# """bool: Whether the dot product is normalized.""" +# return self.thisptr.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) + +# @_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) + +# @_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) + +# @_Compute._computed_property +# def cluster_sizes(self): +# """:math:`(N_{clusters}, )` :class:`np.ndarray`: The sizes of all +# clusters.""" +# return np.asarray(self.thisptr.getClusterSizes()) + +# @_Compute._computed_property +# def largest_cluster_size(self): +# """unsigned int: The largest cluster size.""" +# return self.thisptr.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 + +# @_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) + +# 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) + +# def plot(self, ax=None): +# """Plot solid-like cluster distribution. + +# Args: +# ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If +# :code:`None`, make a new figure and axis +# (Default value = :code:`None`). + +# Returns: +# (: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) + +# 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): +# """Calculates a measure of total rotational autocorrelation. + +# For any calculation of rotational correlations of extended (i.e. non-point) +# particles, encoding the symmetries of these particles is crucial to +# appropriately determining correlations. For systems of anisotropic +# particles in three dimensions, representing such equivalence can be quite +# mathematically challenging. This calculation is based on the hyperspherical +# harmonics as laid out in :cite:`Karas2019`. Generalizations of spherical +# harmonics to four dimensions, hyperspherical harmonics provide a natural +# basis for periodic functions in 4 dimensions just as harmonic functions +# (sines and cosines) or spherical harmonics do in lower dimensions. The idea +# behind this calculation is to embed orientation quaternions into a +# 4-dimensional space and then use hyperspherical harmonics to find +# correlations in a symmetry-aware fashion. + +# The choice of the hyperspherical harmonic parameter :math:`l` determines +# the symmetry of the functions. The output is not a correlation function, +# but rather a scalar value that measures total system orientational +# correlation with an initial state. As such, the output can be treated as an +# order parameter measuring degrees of rotational (de)correlation. For +# analysis of a trajectory, the compute call needs to be +# done at each trajectory frame. + +# Args: +# l (int): +# Order of the hyperspherical harmonic. Must be a positive, even +# integer. +# """ +# cdef freud._order.RotationalAutocorrelation * thisptr + +# def __cinit__(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 + +# def compute(self, ref_orientations, orientations): +# """Calculates the rotational autocorrelation function for a single frame. + +# Args: +# ref_orientations ((:math:`N_{orientations}`, 4) :class:`numpy.ndarray`): +# 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) +# return self + +# @_Compute._computed_property +# def order(self): +# """float: Autocorrelation of the system.""" +# return self.thisptr.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) + +# @property +# def l(self): # noqa: E743 +# """int: The azimuthal quantum number, which defines the order of the +# hyperspherical harmonic.""" +# return self.thisptr.getL() + +# def __repr__(self): +# return "freud.order.{cls}(l={sph_l})".format(cls=type(self).__name__, +# sph_l=self.l) + + +# cdef class ContinuousCoordination(_PairCompute): +# r"""Computes the continuous local coordination number. + +# The :class:`ContinuousCoordination` class implements extensions of the Voronoi +# discrete coordination number to the real numbers. The formulas for the +# various implementations are: + +# Power: + +# .. math:: + +# CN_p = N^{2.0 - m} \sum_{i=1}^{k} +# {\left[\left(\frac{V_i}{V}\right)^{m}\right]}^{-1} + +# Log: + +# .. math:: + +# CN_{log} = \frac{-1}{\log{N}} \sum_{i=1}^{k}\log{\left(\frac{V_i}{V}\right)} + +# Exponential: + +# .. math:: + +# CN_{exp} = \sum_{i=1}^{k}\exp{\left(\frac{V_i}{V} - \frac{1}{N} \right)} + +# where :math:`k` is the number of neighbors a particle has, :math:`V_i` is +# the volume of the pyramid (or area of the triangle in 2D) whose base is the +# Voronoi polytope facet between the central particle and neighbor :math:`i` +# and whose height is half the distance vector, and :math:`V` is the +# volume/area of the Voronoi polytope. + +# Note: +# When using multiple powers, space them out to avoid high correlations. A +# minimum spacing of 2.0 is recommended with even larger values leading to +# less correlation. + +# Args: +# powers (list[float], optional): The powers to compute the continuous +# coordination number for. The default value indicates only compute +# for power 2.0. +# (Default value: None) +# compute_log (`bool`, optional): Whether to compute the log continuous +# coordination number. +# (Default value: :code:`True`) +# compute_exp (`bool`, optional): Whether to compute the exp continuous +# coordination number. +# (Default value: :code:`True`) +# """ +# cdef freud._order.ContinuousCoordination* thisptr + +# def __cinit__(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 + +# def compute(self, system=None, voronoi=None): +# r"""Calculates the local coordination number for the specified points. + +# Example:: + +# >>> import freud +# >>> box, points = freud.data.make_random_system(10, 100, seed=0) +# >>> # Compute ContinuousCoordination +# >>> coord = freud.order.ContinuousCoordination([2, 4], True) +# >>> coord.compute(system=(box, points)) +# freud.order.ContinuousCoordination(...) + +# Args: +# system (optional): +# Any object that is a valid argument to +# :class:`freud.locality.NeighborQuery.from_system`. +# (Default value: None). +# voronoi (:class:`freud.locality.Voronoi`, optional): +# A precomputed Voronoi compute object. If provided, the object is +# 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.") +# 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.") +# cpp_voronoi = voronoi +# self.thisptr.compute(cpp_voronoi.thisptr) +# return self + +# @_Compute._computed_property +# def particle_order(self): +# """(:math:`(N_{points}, N_{coord}`) :class:`numpy.ndarray`: \ +# coordination of points per query point. + +# Coordination numbers are in order of selected powers, log, and exp. +# """ +# return self.coordination + +# @_Compute._computed_property +# def coordination(self): +# """(:math:`(N_{points}, N_{coord}`) :class:`numpy.ndarray`): \ +# coordination of points per query point. + +# 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) + +# @property +# def powers(self): +# """list[float]: The powers to compute the continuous coordination number. + +# Changes to this property are not reflected when computing coordination +# numbers. +# """ +# return self.thisptr.getPowers() + +# @property +# def compute_log(self): +# """bool: Whether to compute the log continuous coordination number.""" +# return self.thisptr.getComputeLog() + +# @property +# def compute_exp(self): +# """bool: Whether to compute the exponential coordination number.""" +# return self.thisptr.getComputeExp() + +# @property +# def number_of_coordinations(self): +# """int: The number of coordination numbers computed.""" +# return self.thisptr.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, +# ) diff --git a/freud/order/export-Nematic.cc b/freud/order/export-Nematic.cc index bb08b94e8..588e137fa 100644 --- a/freud/order/export-Nematic.cc +++ b/freud/order/export-Nematic.cc @@ -2,7 +2,7 @@ #include #include #include // NOLINT(misc-include-cleaner): used implicitly - +#include // NOLINT(misc-include-cleaner): used implicitly #include // #include "BondHistogramCompute.h" @@ -27,6 +27,11 @@ void computeNematic(const std::shared_ptr& self, 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 @@ -41,7 +46,12 @@ void export_Nematic(nanobind::module_& m) { nanobind::class_(m, "Nematic") .def(nanobind::init<>()) - .def("compute", &wrap::computeNematic, nanobind::arg("orientations")); + .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 From addde43b12699f1d974475ee29665338be782d35 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 11 Sep 2024 13:26:08 -0400 Subject: [PATCH 07/91] Convert to numpy array --- freud/order.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freud/order.py b/freud/order.py index 4dbdaab43..257760d19 100644 --- a/freud/order.py +++ b/freud/order.py @@ -235,13 +235,13 @@ 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 self._cpp_obj.getParticleTensor() + 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 self._cpp_obj.getNematicTensor() + return self._cpp_obj.getNematicTensor().toNumpyArray() def __repr__(self): return f"freud.order.{type(self).__name__}()" From 5f40d6e96646ff56807ecf4377f6f3fa3395e2bd Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 11 Sep 2024 11:28:59 -0400 Subject: [PATCH 08/91] Update order/Nematic --- freud/order/Nematic.cc | 25 ++++++++++++++----------- freud/order/Nematic.h | 16 ++++++++++------ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/freud/order/Nematic.cc b/freud/order/Nematic.cc index cf8473d12..4303792d8 100644 --- a/freud/order/Nematic.cc +++ b/freud/order/Nematic.cc @@ -1,6 +1,9 @@ // 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 "Nematic.h" @@ -17,12 +20,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 +43,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 +71,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..8d80e695c 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,9 @@ 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 From 89bac0c63e7245e14567d9695b648213d8eca9cf Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 11 Sep 2024 11:29:09 -0400 Subject: [PATCH 09/91] Export nematic and module order --- freud/order/export-Nematic.cc | 49 +++++++++++++++++++++++++++++++++++ freud/order/module-order.cc | 15 +++++++++++ 2 files changed, 64 insertions(+) create mode 100644 freud/order/export-Nematic.cc create mode 100644 freud/order/module-order.cc diff --git a/freud/order/export-Nematic.cc b/freud/order/export-Nematic.cc new file mode 100644 index 000000000..bb08b94e8 --- /dev/null +++ b/freud/order/export-Nematic.cc @@ -0,0 +1,49 @@ +#include +#include +#include +#include // NOLINT(misc-include-cleaner): used implicitly + +#include + +// #include "BondHistogramCompute.h" +// #include "NeighborList.h" +// #include "NeighborQuery.h" +#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); +} + +}; // namespace wrap + + +namespace detail { + +// void export_PMFT(nanobind::module_& m) +// { +// nanobind::class_(m, "PMFT").def("getPCF", &PMFT::getPCF); +// } + +void export_Nematic(nanobind::module_& m) +{ + nanobind::class_(m, "Nematic") + .def(nanobind::init<>()) + .def("compute", &wrap::computeNematic, nanobind::arg("orientations")); +} + +} // namespace detail + +}; }; // end namespace freud::pmft diff --git a/freud/order/module-order.cc b/freud/order/module-order.cc new file mode 100644 index 000000000..b6fc0356b --- /dev/null +++ b/freud/order/module-order.cc @@ -0,0 +1,15 @@ +#include +#include + +namespace freud::order::detail { + // void export_PMFT(nanobind::module_& m); + // void export_PMFTXY(nanobind::module_& m); + void export_Nematic(nanobind::module_& m); +} // namespace freud::pmft::detail + +using namespace freud::order::detail; + +NB_MODULE(_order, module) // NOLINT(misc-use-anonymous-namespace): caused by nanobind +{ + export_Nematic(module); +} From 24eec2218ed2783e4e50e99de9bda0210f46661e Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 11 Sep 2024 11:29:16 -0400 Subject: [PATCH 10/91] Update cmakelists --- freud/CMakeLists.txt | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/freud/CMakeLists.txt b/freud/CMakeLists.txt index 5cfaccaab..32fe48121 100644 --- a/freud/CMakeLists.txt +++ b/freud/CMakeLists.txt @@ -89,15 +89,15 @@ add_library( ${VOROPP_SOURCE_DIR}/wall.cc ${VOROPP_SOURCE_DIR}/pre_container.cc ${VOROPP_SOURCE_DIR}/container_prd.cc - # order + 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/Nematic.cc + order/Nematic.h # order/RotationalAutocorrelation.cc # order/RotationalAutocorrelation.h # order/SolidLiquid.cc @@ -173,6 +173,14 @@ 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 +) +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 +209,14 @@ 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 +228,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) From 770a76993037ffae52c5ac92306e065b9fcfb5e6 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 11 Sep 2024 11:29:25 -0400 Subject: [PATCH 11/91] Move order.pyx to order.py --- freud/{order.pyx => order.py} | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) rename freud/{order.pyx => order.py} (99%) diff --git a/freud/order.pyx b/freud/order.py similarity index 99% rename from freud/order.pyx rename to freud/order.py index 7100d919a..f3828a4f3 100644 --- a/freud/order.pyx +++ b/freud/order.py @@ -10,13 +10,13 @@ Fourier Transforms. """ -from freud.util cimport _Compute, quat, vec3 +from freud.util import _Compute #, quat, vec3 from freud.errors import FreudDeprecationWarning -from cython.operator cimport dereference +# from cython.operator cimport dereference -from freud.locality cimport _PairCompute +# from freud.locality cimport _PairCompute import collections.abc import logging @@ -27,11 +27,11 @@ import freud.locality -cimport numpy as np +import numpy as np -cimport freud._order -cimport freud.locality -cimport freud.util +import freud._order +# cimport freud.locality +# cimport freud.util logger = logging.getLogger(__name__) From 2bb30bbd9a6088832f51121248a66cbc4b1a510d Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 11 Sep 2024 11:32:36 -0400 Subject: [PATCH 12/91] Update init.py --- freud/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freud/__init__.py b/freud/__init__.py index f8d9f4809..2fbb32be3 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, data, locality, order, parallel, pmft from .box import Box from .locality import AABBQuery, LinkCell, NeighborList from .parallel import NumThreads, get_num_threads, set_num_threads @@ -23,7 +23,7 @@ # "environment", "interface", "locality", - "msd", + # "msd", # "order", "parallel", "pmft", From c77b5effe8afd3dd032a1b48f6c488261a728511 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 11 Sep 2024 13:18:40 -0400 Subject: [PATCH 13/91] Update python side for Nematic order --- freud/order.py | 2034 ++++++++++++++++----------------- freud/order/export-Nematic.cc | 14 +- 2 files changed, 1024 insertions(+), 1024 deletions(-) diff --git a/freud/order.py b/freud/order.py index f3828a4f3..4dbdaab43 100644 --- a/freud/order.py +++ b/freud/order.py @@ -37,129 +37,131 @@ # 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): - r"""Compute the cubatic order parameter :cite:`Haji_Akbari_2015` for a system of - particles using simulated annealing instead of Newton-Raphson root finding. - - Args: - t_initial (float): - Starting temperature. - t_final (float): - Final temperature. - scale (float): - Scaling factor to reduce temperature. - n_replicates (unsigned int, optional): - Number of replicate simulated annealing runs. - (Default value = :code:`1`). - 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): - 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 - - def compute(self, orientations): - r"""Calculates the per-particle and global order parameter. - - Args: - 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) - return self - - @property - def t_initial(self): - """float: The value of the initial temperature.""" - return self.thisptr.getTInitial() - - @property - def t_final(self): - """float: The value of the final temperature.""" - return self.thisptr.getTFinal() - - @property - def scale(self): - """float: The scale.""" - return self.thisptr.getScale() - - @property - def n_replicates(self): - """unsigned int: Number of replicate simulated annealing runs.""" - return self.thisptr.getNReplicates() - - @property - def seed(self): - """unsigned int: Random seed to use in calculations.""" - return self.thisptr.getSeed() - - @_Compute._computed_property - def order(self): - """float: Cubatic order parameter of the system.""" - return self.thisptr.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) - - @_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) - - @_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) - - @_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) - - 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) - - -cdef class Nematic(_Compute): +# np.import_array() + +print("ADFHAFGJAKFHAJ") + +# cdef 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. + +# Args: +# t_initial (float): +# Starting temperature. +# t_final (float): +# Final temperature. +# scale (float): +# Scaling factor to reduce temperature. +# n_replicates (unsigned int, optional): +# Number of replicate simulated annealing runs. +# (Default value = :code:`1`). +# 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): +# 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 + +# def compute(self, orientations): +# r"""Calculates the per-particle and global order parameter. + +# Args: +# 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) +# return self + +# @property +# def t_initial(self): +# """float: The value of the initial temperature.""" +# return self.thisptr.getTInitial() + +# @property +# def t_final(self): +# """float: The value of the final temperature.""" +# return self.thisptr.getTFinal() + +# @property +# def scale(self): +# """float: The scale.""" +# return self.thisptr.getScale() + +# @property +# def n_replicates(self): +# """unsigned int: Number of replicate simulated annealing runs.""" +# return self.thisptr.getNReplicates() + +# @property +# def seed(self): +# """unsigned int: Random seed to use in calculations.""" +# return self.thisptr.getSeed() + +# @_Compute._computed_property +# def order(self): +# """float: Cubatic order parameter of the system.""" +# return self.thisptr.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) + +# @_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) + +# @_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) + +# @_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) + +# 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) + + +class Nematic(_Compute): r"""Compute the nematic order parameter for a system of particles. Note: @@ -183,13 +185,9 @@ def __repr__(self): freud.order.Nematic() """ - cdef freud._order.Nematic *thisptr + def __init__(self): + self._cpp_obj = freud._order.Nematic() - def __cinit__(self): - self.thisptr = new freud._order.Nematic() - - def __dealloc__(self): - del self.thisptr def compute(self, orientations): r"""Calculates the per-particle and global order parameter. @@ -217,920 +215,912 @@ def compute(self, orientations): 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) + 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) + return self._cpp_obj.getParticleTensor() @_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) - - def __repr__(self): - return "freud.order.{cls}()".format(cls=type(self).__name__) - - -cdef 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 - :math:`k = 6`) is analogous to Steinhardt order parameters, and is used to - measure order in the bonds of 2D systems. - - The :math:`k`-atic order parameter for a particle :math:`i` and its - :math:`N_b` neighbors :math:`j` is given by: - - :math:`\psi_k \left( i \right) = \frac{1}{N_b} - \sum \limits_{j=1}^{N_b} e^{i k \phi_{ij}}` - - The parameter :math:`k` governs the symmetry of the order parameter and - typically matches the number of neighbors to be found for each particle. - The quantity :math:`\phi_{ij}` is the angle between the - vector :math:`r_{ij}` and :math:`\left(1, 0\right)`. - - If the weighted mode is enabled, contributions of each neighbor are - weighted. Neighbor weights :math:`w_{ij}` default to 1 but are defined for a - :class:`freud.locality.NeighborList` from :class:`freud.locality.Voronoi` - or one with user-provided weights. The formula is modified as follows: - - :math:`\psi'_k \left( i \right) = \frac{1}{\sum_{j=1}^{N_b} w_{ij}} - \sum \limits_{j=1}^{N_b} w_{ij} e^{i k \phi_{ij}}` - - The hexatic order parameter as written above is **complex-valued**. The - **magnitude** of the complex value, - :code:`np.abs(hex_order.particle_order)`, is frequently what is desired - when determining the :math:`k`-atic order for each particle. The complex - phase angle :code:`np.angle(hex_order.particle_order)` indicates the - orientation of the bonds as an angle measured counterclockwise from the - vector :math:`\left(1, 0\right)`. The complex valued order parameter is - not rotationally invariant because of this phase angle, but the magnitude - *is* rotationally invariant. - - .. note:: - **2D:** :class:`freud.order.Hexatic` is only defined for 2D systems. - The points must be passed in as :code:`[x, y, 0]`. - - Args: - k (unsigned int, optional): - Symmetry of order parameter (Default value = :code:`6`). - weighted (bool, optional): - Determines whether to use neighbor weights in the computation of - spherical harmonics over neighbors. If enabled and used with a - 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 compute(self, system, neighbors=None): - r"""Calculates the hexatic order parameter. - - Example:: - - >>> box, points = freud.data.make_random_system( - ... box_size=10, num_points=100, is2D=True, seed=0) - >>> # Compute the hexatic (6-fold) order for the 2D system - >>> hex_order = freud.order.Hexatic(k=6) - >>> hex_order.compute(system=(box, points)) - freud.order.Hexatic(...) - >>> print(hex_order.particle_order) - [...] - - Args: - system: - Any object that is a valid argument to - :class:`freud.locality.NeighborQuery.from_system`. - neighbors (:class:`freud.locality.NeighborList` or dict, optional): - Either a :class:`NeighborList ` of - neighbor pairs to use in the calculation, or a dictionary of - `query arguments - `_ - (Default value: None). - """ # noqa: E501 - cdef: - freud.locality.NeighborQuery nq - freud.locality.NeighborList nlist - freud.locality._QueryArgs qargs - const float[:, ::1] l_query_points - unsigned int num_query_points - - nq, nlist, qargs, l_query_points, num_query_points = \ - self._preprocess_arguments(system, neighbors=neighbors) - self.thisptr.compute(nlist.get_ptr(), - nq.get_ptr(), dereference(qargs.thisptr)) - return self - - @property - def default_query_args(self): - """The default query arguments are - :code:`{'mode': 'nearest', 'num_neighbors': self.k}`.""" - return dict(mode="nearest", num_neighbors=self.k) - - @_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.getOrder(), - freud.util.arr_type_t.COMPLEX_FLOAT) - - @property - def k(self): - """unsigned int: Symmetry of the order parameter.""" - return self.thisptr.getK() - - @property - def weighted(self): - """bool: Whether neighbor weights were used in the computation.""" - return self.thisptr.isWeighted() - - def __repr__(self): - return "freud.order.{cls}(k={k}, weighted={weighted})".format( - cls=type(self).__name__, k=self.k, weighted=self.weighted) - - def plot(self, ax=None): - """Plot order parameter distribution. - - Args: - ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If - :code:`None`, make a new figure and axis - (Default value = :code:`None`). - - Returns: - (: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) - - return freud.plot.histogram_plot( - np.absolute(self.particle_order), - title="Hexatic Order Parameter " + xlabel, - xlabel=xlabel, - ylabel=r"Number of particles", - 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): - 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`. - - Implements the local rotationally invariant :math:`q_l` or :math:`w_l` - order parameter described by Steinhardt. - - First, we describe the computation of :math:`q_l(i)`. For a particle :math:`i`, - we calculate the quantity :math:`q_{lm}` by summing the spherical harmonics - between particle :math:`i` and its neighbors :math:`j` in a local region: - - .. math:: - - q_{lm}(i) = \frac{1}{N_b} \sum \limits_{j=1}^{N_b} - Y_{lm}(\theta(\vec{r}_{ij}), \phi(\vec{r}_{ij})) - - Then the :math:`q_l` order parameter is computed by combining the :math:`q_{lm}` - in a rotationally invariant fashion to remove local orientational order: - - .. math:: - - q_l(i) = \sqrt{\frac{4\pi}{2l+1} \sum \limits_{m=-l}^{l} - |q_{lm}(i)|^2 } - - If the ``wl`` parameter is ``True``, this class computes the quantity - :math:`w_l`, defined as a weighted average over the - :math:`q_{lm}(i)` values using `Wigner 3-j symbols - `__ (related to `Clebsch-Gordan - coefficients - `__). - The resulting combination is rotationally invariant: - - .. math:: - - w_l(i) = \sum \limits_{m_1 + m_2 + m_3 = 0} \begin{pmatrix} - l & l & l \\ - m_1 & m_2 & m_3 - \end{pmatrix} - q_{lm_1}(i) q_{lm_2}(i) q_{lm_3}(i) - - If ``wl`` is ``True``, then setting the ``wl_normalize`` parameter to ``True`` will - normalize the :math:`w_l` order parameter as follows (if ``wl=False``, - ``wl_normalize`` has no effect): - - .. math:: - - w_l(i) = \frac{ - \sum \limits_{m_1 + m_2 + m_3 = 0} \begin{pmatrix} - l & l & l \\ - m_1 & m_2 & m_3 - \end{pmatrix} - q_{lm_1}(i) q_{lm_2}(i) q_{lm_3}(i)} - {\left(\sum \limits_{m=-l}^{l} |q_{lm}(i)|^2 \right)^{3/2}} - - If ``average`` is ``True``, the class computes a variant of this order - parameter that performs an average over the first and second shell combined - :cite:`Lechner_2008`. To compute this parameter, we perform a second - averaging over the first neighbor shell of the particle to implicitly - include information about the second neighbor shell. This averaging is - performed by replacing the value :math:`q_{lm}(i)` in the original - definition by :math:`\overline{q}_{lm}(i)`, the average value of - :math:`q_{lm}(k)` over all the :math:`N_b` neighbors :math:`k` - of particle :math:`i`, including particle :math:`i` itself: - - .. math:: - \overline{q}_{lm}(i) = \frac{1}{N_b} \sum \limits_{k=0}^{N_b} - q_{lm}(k) - - If ``weighted`` is True, the contributions of each neighbor are weighted. - Neighbor weights :math:`w_{ij}` are defined for a - :class:`freud.locality.NeighborList` obtained from - :class:`freud.locality.Voronoi` or one with user-provided weights, and - default to 1 if not otherwise provided. The formulas are modified as - follows, replacing :math:`q_{lm}(i)` with the weighted value - :math:`q'_{lm}(i)`: - - .. math:: - - q'_{lm}(i) = \frac{1}{\sum_{j=1}^{N_b} w_{ij}} - \sum \limits_{j=1}^{N_b} w_{ij} Y_{lm}(\theta(\vec{r}_{ij}), - \phi(\vec{r}_{ij})) - - .. note:: - The value of per-particle order parameter will be set to NaN for - particles with no neighbors. We choose this value rather than setting - the order parameter to 0 because in more complex order parameter - calculations (such as when computing the :math:`w_l`), it is possible - to observe a value of 0 for the per-particle order parameter even with - a finite number of neighbors. If you would like to ignore this - distinction, you can mask the output order parameter values using - NumPy: :code:`numpy.nan_to_num(particle_order)`. - - Args: - l (unsigned int or sequence of unsigned int): - One or more spherical harmonic quantum number l's used to compute - the Steinhardt order parameter. - average (bool, optional): - Determines whether to calculate the averaged Steinhardt order - parameter (Default value = :code:`False`). - wl (bool, optional): - Determines whether to use the :math:`w_l` version of the Steinhardt - order parameter (Default value = :code:`False`). - weighted (bool, optional): - Determines whether to use neighbor weights in the computation of - spherical harmonics over neighbors. If enabled and used with a - Voronoi neighbor list, this results in the 3D Minkowski Structure - Metrics :math:`q'_l` :cite:`Mickel2013` (Default value = - :code:`False`). - 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): - 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 - - @property - def average(self): - """bool: Whether the averaged Steinhardt order parameter was - calculated.""" - return self.thisptr.isAverage() - - @property - def wl(self): - """bool: Whether the :math:`w_l` version of the Steinhardt order - parameter was used.""" - return self.thisptr.isWl() - - @property - def weighted(self): - """bool: Whether neighbor weights were used in the computation.""" - return self.thisptr.isWeighted() - - @property - def wl_normalize(self): - return self.thisptr.isWlNormalized() - - @property - def l(self): # noqa: E743 - """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()) - return ls[0] if len(ls) == 1 else ls - - @_Compute._computed_property - def order(self): - r"""float: The system wide normalization of the order parameter, - computed by averaging the :math:`q_{lm}` values (or - :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()) - return order[0] if len(order) == 1 else order - - @_Compute._computed_property - 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 - - @_Compute._computed_property - def ql(self): - """:math:`\\left(N_{particles}, N_l\\right)` :class:`numpy.ndarray`: - :math:`q_l` Steinhardt order parameter for each particle (filled with - :code:`nan` for particles with no neighbors). This is always available, - 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 - - @_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())] - return qlm_list if len(qlm_list) > 1 else qlm_list[0] - - def compute(self, system, neighbors=None): - r"""Compute the order parameter. - - Example:: - - >>> box, points = freud.data.make_random_system(10, 100, seed=0) - >>> ql = freud.order.Steinhardt(l=6) - >>> ql.compute((box, points), {'r_max':3}) - freud.order.Steinhardt(l=6, ...) - - Args: - system: - Any object that is a valid argument to - :class:`freud.locality.NeighborQuery.from_system`. - neighbors (:class:`freud.locality.NeighborList` or dict, optional): - Either a :class:`NeighborList ` of - neighbor pairs to use in the calculation, or a dictionary of - `query arguments - `_ - (Default value: None). - """ # noqa: E501 - cdef: - freud.locality.NeighborQuery nq - freud.locality.NeighborList nlist - freud.locality._QueryArgs qargs - const float[:, ::1] l_query_points - unsigned int num_query_points - - nq, nlist, qargs, l_query_points, num_query_points = \ - self._preprocess_arguments(system, neighbors=neighbors) - - self.thisptr.compute(nlist.get_ptr(), - nq.get_ptr(), - dereference(qargs.thisptr)) - 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) - - def plot(self, ax=None): - """Plot order parameter distribution. - - Args: - ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If - :code:`None`, make a new figure and axis - (Default value = :code:`None`). - - Returns: - (:class:`matplotlib.axes.Axes`): Axis with the plot. - """ - import freud.plot - - ls = self.l - if not isinstance(ls, list): - ls = [ls] - - 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 '') - for sph_l in ls - ] - xlabel = ', '.join(legend_labels) - - # Don't print legend if only one l requested. - if len(legend_labels) == 1: - legend_labels = None - - return freud.plot.histogram_plot( - self.particle_order, - title="Steinhardt Order Parameter " + xlabel, - xlabel=xlabel, - ylabel=r"Number of particles", - ax=ax, - 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): - r"""Identifies solid-like clusters using dot products of :math:`q_{lm}`. - - The solid-liquid order parameter :cite:`Wolde:1995aa,Filion_2010` uses a - Steinhardt-like approach to identify solid-like particles. First, a bond - parameter :math:`q_l(i, j)` is computed for each neighbor bond. - - If :code:`normalize_q` is true (default), the bond parameter is given by - :math:`q_l(i, j) = \frac{\sum \limits_{m=-l}^{l} \text{Re}~q_{lm}(i) q_{lm}^*(j)} - {\sqrt{\sum \limits_{m=-l}^{l} \lvert q_{lm}(i) \rvert^2} - \sqrt{\sum \limits_{m=-l}^{l} \lvert q_{lm}(j) \rvert^2}}` - - If :code:`normalize_q` is false, then the denominator of the above - expression is left out. - - Next, the bonds are filtered to keep only "solid-like" bonds with - :math:`q_l(i, j)` above a cutoff value :math:`q_{threshold}`. - - If a particle has more than :math:`S_{threshold}` solid-like bonds, then - the particle is considered solid-like. Finally, solid-like particles are - clustered. - - Args: - l (unsigned int): - Spherical harmonic quantum number l. - q_threshold (float): - Value of dot product threshold when evaluating - :math:`q_l(i, j)` to determine if a bond is solid-like. For - :math:`l=6`, 0.7 is generally good for FCC or BCC structures - :cite:`Filion_2010`. - solid_threshold (unsigned int): - Minimum required number of adjacent solid-like bonds for a particle - to be considered solid-like for clustering. For :math:`l=6`, 6-8 - is generally good for FCC or BCC structures. - 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 compute(self, system, neighbors=None): - r"""Compute the order parameter. - - Args: - system: - Any object that is a valid argument to - :class:`freud.locality.NeighborQuery.from_system`. - neighbors (:class:`freud.locality.NeighborList` or dict, optional): - Either a :class:`NeighborList ` of - neighbor pairs to use in the calculation, or a dictionary of - `query arguments - `_ - (Default value: None). - """ - 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)) - return self - - @property - def l(self): # noqa: E743 - """unsigned int: Spherical harmonic quantum number l.""" - return self.thisptr.getL() - - @property - def q_threshold(self): - """float: Value of dot product threshold.""" - return self.thisptr.getQThreshold() - - @property - def solid_threshold(self): - """float: Value of number-of-bonds threshold.""" - return self.thisptr.getSolidThreshold() - - @property - def normalize_q(self): - """bool: Whether the dot product is normalized.""" - return self.thisptr.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) - - @_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) - - @_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) - - @_Compute._computed_property - def cluster_sizes(self): - """:math:`(N_{clusters}, )` :class:`np.ndarray`: The sizes of all - clusters.""" - return np.asarray(self.thisptr.getClusterSizes()) - - @_Compute._computed_property - def largest_cluster_size(self): - """unsigned int: The largest cluster size.""" - return self.thisptr.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 - - @_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) - - 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) - - def plot(self, ax=None): - """Plot solid-like cluster distribution. - - Args: - ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If - :code:`None`, make a new figure and axis - (Default value = :code:`None`). - - Returns: - (: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) - - 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): - """Calculates a measure of total rotational autocorrelation. - - For any calculation of rotational correlations of extended (i.e. non-point) - particles, encoding the symmetries of these particles is crucial to - appropriately determining correlations. For systems of anisotropic - particles in three dimensions, representing such equivalence can be quite - mathematically challenging. This calculation is based on the hyperspherical - harmonics as laid out in :cite:`Karas2019`. Generalizations of spherical - harmonics to four dimensions, hyperspherical harmonics provide a natural - basis for periodic functions in 4 dimensions just as harmonic functions - (sines and cosines) or spherical harmonics do in lower dimensions. The idea - behind this calculation is to embed orientation quaternions into a - 4-dimensional space and then use hyperspherical harmonics to find - correlations in a symmetry-aware fashion. - - The choice of the hyperspherical harmonic parameter :math:`l` determines - the symmetry of the functions. The output is not a correlation function, - but rather a scalar value that measures total system orientational - correlation with an initial state. As such, the output can be treated as an - order parameter measuring degrees of rotational (de)correlation. For - analysis of a trajectory, the compute call needs to be - done at each trajectory frame. - - Args: - l (int): - Order of the hyperspherical harmonic. Must be a positive, even - integer. - """ - cdef freud._order.RotationalAutocorrelation * thisptr - - def __cinit__(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 - - def compute(self, ref_orientations, orientations): - """Calculates the rotational autocorrelation function for a single frame. - - Args: - ref_orientations ((:math:`N_{orientations}`, 4) :class:`numpy.ndarray`): - 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) - return self - - @_Compute._computed_property - def order(self): - """float: Autocorrelation of the system.""" - return self.thisptr.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) - - @property - def l(self): # noqa: E743 - """int: The azimuthal quantum number, which defines the order of the - hyperspherical harmonic.""" - return self.thisptr.getL() - - def __repr__(self): - return "freud.order.{cls}(l={sph_l})".format(cls=type(self).__name__, - sph_l=self.l) - - -cdef class ContinuousCoordination(_PairCompute): - r"""Computes the continuous local coordination number. - - The :class:`ContinuousCoordination` class implements extensions of the Voronoi - discrete coordination number to the real numbers. The formulas for the - various implementations are: - - Power: - - .. math:: - - CN_p = N^{2.0 - m} \sum_{i=1}^{k} - {\left[\left(\frac{V_i}{V}\right)^{m}\right]}^{-1} - - Log: - - .. math:: - - CN_{log} = \frac{-1}{\log{N}} \sum_{i=1}^{k}\log{\left(\frac{V_i}{V}\right)} - - Exponential: - - .. math:: - - CN_{exp} = \sum_{i=1}^{k}\exp{\left(\frac{V_i}{V} - \frac{1}{N} \right)} - - where :math:`k` is the number of neighbors a particle has, :math:`V_i` is - the volume of the pyramid (or area of the triangle in 2D) whose base is the - Voronoi polytope facet between the central particle and neighbor :math:`i` - and whose height is half the distance vector, and :math:`V` is the - volume/area of the Voronoi polytope. - - Note: - When using multiple powers, space them out to avoid high correlations. A - minimum spacing of 2.0 is recommended with even larger values leading to - less correlation. - - Args: - powers (list[float], optional): The powers to compute the continuous - coordination number for. The default value indicates only compute - for power 2.0. - (Default value: None) - compute_log (`bool`, optional): Whether to compute the log continuous - coordination number. - (Default value: :code:`True`) - compute_exp (`bool`, optional): Whether to compute the exp continuous - coordination number. - (Default value: :code:`True`) - """ - cdef freud._order.ContinuousCoordination* thisptr - - def __cinit__(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 - - def compute(self, system=None, voronoi=None): - r"""Calculates the local coordination number for the specified points. - - Example:: - - >>> import freud - >>> box, points = freud.data.make_random_system(10, 100, seed=0) - >>> # Compute ContinuousCoordination - >>> coord = freud.order.ContinuousCoordination([2, 4], True) - >>> coord.compute(system=(box, points)) - freud.order.ContinuousCoordination(...) - - Args: - system (optional): - Any object that is a valid argument to - :class:`freud.locality.NeighborQuery.from_system`. - (Default value: None). - voronoi (:class:`freud.locality.Voronoi`, optional): - A precomputed Voronoi compute object. If provided, the object is - 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.") - 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.") - cpp_voronoi = voronoi - self.thisptr.compute(cpp_voronoi.thisptr) - return self - - @_Compute._computed_property - def particle_order(self): - """(:math:`(N_{points}, N_{coord}`) :class:`numpy.ndarray`: \ - coordination of points per query point. - - Coordination numbers are in order of selected powers, log, and exp. - """ - return self.coordination - - @_Compute._computed_property - def coordination(self): - """(:math:`(N_{points}, N_{coord}`) :class:`numpy.ndarray`): \ - coordination of points per query point. - - 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) - - @property - def powers(self): - """list[float]: The powers to compute the continuous coordination number. - - Changes to this property are not reflected when computing coordination - numbers. - """ - return self.thisptr.getPowers() - - @property - def compute_log(self): - """bool: Whether to compute the log continuous coordination number.""" - return self.thisptr.getComputeLog() - - @property - def compute_exp(self): - """bool: Whether to compute the exponential coordination number.""" - return self.thisptr.getComputeExp() - - @property - def number_of_coordinations(self): - """int: The number of coordination numbers computed.""" - return self.thisptr.getNumberOfCoordinations() + return self._cpp_obj.getNematicTensor() 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, - ) + return f"freud.order.{type(self).__name__}()" + + +# cdef 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 +# :math:`k = 6`) is analogous to Steinhardt order parameters, and is used to +# measure order in the bonds of 2D systems. + +# The :math:`k`-atic order parameter for a particle :math:`i` and its +# :math:`N_b` neighbors :math:`j` is given by: + +# :math:`\psi_k \left( i \right) = \frac{1}{N_b} +# \sum \limits_{j=1}^{N_b} e^{i k \phi_{ij}}` + +# The parameter :math:`k` governs the symmetry of the order parameter and +# typically matches the number of neighbors to be found for each particle. +# The quantity :math:`\phi_{ij}` is the angle between the +# vector :math:`r_{ij}` and :math:`\left(1, 0\right)`. + +# If the weighted mode is enabled, contributions of each neighbor are +# weighted. Neighbor weights :math:`w_{ij}` default to 1 but are defined for a +# :class:`freud.locality.NeighborList` from :class:`freud.locality.Voronoi` +# or one with user-provided weights. The formula is modified as follows: + +# :math:`\psi'_k \left( i \right) = \frac{1}{\sum_{j=1}^{N_b} w_{ij}} +# \sum \limits_{j=1}^{N_b} w_{ij} e^{i k \phi_{ij}}` + +# The hexatic order parameter as written above is **complex-valued**. The +# **magnitude** of the complex value, +# :code:`np.abs(hex_order.particle_order)`, is frequently what is desired +# when determining the :math:`k`-atic order for each particle. The complex +# phase angle :code:`np.angle(hex_order.particle_order)` indicates the +# orientation of the bonds as an angle measured counterclockwise from the +# vector :math:`\left(1, 0\right)`. The complex valued order parameter is +# not rotationally invariant because of this phase angle, but the magnitude +# *is* rotationally invariant. + +# .. note:: +# **2D:** :class:`freud.order.Hexatic` is only defined for 2D systems. +# The points must be passed in as :code:`[x, y, 0]`. + +# Args: +# k (unsigned int, optional): +# Symmetry of order parameter (Default value = :code:`6`). +# weighted (bool, optional): +# Determines whether to use neighbor weights in the computation of +# spherical harmonics over neighbors. If enabled and used with a +# 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 compute(self, system, neighbors=None): +# r"""Calculates the hexatic order parameter. + +# Example:: + +# >>> box, points = freud.data.make_random_system( +# ... box_size=10, num_points=100, is2D=True, seed=0) +# >>> # Compute the hexatic (6-fold) order for the 2D system +# >>> hex_order = freud.order.Hexatic(k=6) +# >>> hex_order.compute(system=(box, points)) +# freud.order.Hexatic(...) +# >>> print(hex_order.particle_order) +# [...] + +# Args: +# system: +# Any object that is a valid argument to +# :class:`freud.locality.NeighborQuery.from_system`. +# neighbors (:class:`freud.locality.NeighborList` or dict, optional): +# Either a :class:`NeighborList ` of +# neighbor pairs to use in the calculation, or a dictionary of +# `query arguments +# `_ +# (Default value: None). +# """ # noqa: E501 +# cdef: +# freud.locality.NeighborQuery nq +# freud.locality.NeighborList nlist +# freud.locality._QueryArgs qargs +# const float[:, ::1] l_query_points +# unsigned int num_query_points + +# nq, nlist, qargs, l_query_points, num_query_points = \ +# self._preprocess_arguments(system, neighbors=neighbors) +# self.thisptr.compute(nlist.get_ptr(), +# nq.get_ptr(), dereference(qargs.thisptr)) +# return self + +# @property +# def default_query_args(self): +# """The default query arguments are +# :code:`{'mode': 'nearest', 'num_neighbors': self.k}`.""" +# return dict(mode="nearest", num_neighbors=self.k) + +# @_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.getOrder(), +# freud.util.arr_type_t.COMPLEX_FLOAT) + +# @property +# def k(self): +# """unsigned int: Symmetry of the order parameter.""" +# return self.thisptr.getK() + +# @property +# def weighted(self): +# """bool: Whether neighbor weights were used in the computation.""" +# return self.thisptr.isWeighted() + +# def __repr__(self): +# return "freud.order.{cls}(k={k}, weighted={weighted})".format( +# cls=type(self).__name__, k=self.k, weighted=self.weighted) + +# def plot(self, ax=None): +# """Plot order parameter distribution. + +# Args: +# ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If +# :code:`None`, make a new figure and axis +# (Default value = :code:`None`). + +# Returns: +# (: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) + +# return freud.plot.histogram_plot( +# np.absolute(self.particle_order), +# title="Hexatic Order Parameter " + xlabel, +# xlabel=xlabel, +# ylabel=r"Number of particles", +# 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): +# 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`. + +# Implements the local rotationally invariant :math:`q_l` or :math:`w_l` +# order parameter described by Steinhardt. + +# First, we describe the computation of :math:`q_l(i)`. For a particle :math:`i`, +# we calculate the quantity :math:`q_{lm}` by summing the spherical harmonics +# between particle :math:`i` and its neighbors :math:`j` in a local region: + +# .. math:: + +# q_{lm}(i) = \frac{1}{N_b} \sum \limits_{j=1}^{N_b} +# Y_{lm}(\theta(\vec{r}_{ij}), \phi(\vec{r}_{ij})) + +# Then the :math:`q_l` order parameter is computed by combining the :math:`q_{lm}` +# in a rotationally invariant fashion to remove local orientational order: + +# .. math:: + +# q_l(i) = \sqrt{\frac{4\pi}{2l+1} \sum \limits_{m=-l}^{l} +# |q_{lm}(i)|^2 } + +# If the ``wl`` parameter is ``True``, this class computes the quantity +# :math:`w_l`, defined as a weighted average over the +# :math:`q_{lm}(i)` values using `Wigner 3-j symbols +# `__ (related to `Clebsch-Gordan +# coefficients +# `__). +# The resulting combination is rotationally invariant: + +# .. math:: + +# w_l(i) = \sum \limits_{m_1 + m_2 + m_3 = 0} \begin{pmatrix} +# l & l & l \\ +# m_1 & m_2 & m_3 +# \end{pmatrix} +# q_{lm_1}(i) q_{lm_2}(i) q_{lm_3}(i) + +# If ``wl`` is ``True``, then setting the ``wl_normalize`` parameter to ``True`` will +# normalize the :math:`w_l` order parameter as follows (if ``wl=False``, +# ``wl_normalize`` has no effect): + +# .. math:: + +# w_l(i) = \frac{ +# \sum \limits_{m_1 + m_2 + m_3 = 0} \begin{pmatrix} +# l & l & l \\ +# m_1 & m_2 & m_3 +# \end{pmatrix} +# q_{lm_1}(i) q_{lm_2}(i) q_{lm_3}(i)} +# {\left(\sum \limits_{m=-l}^{l} |q_{lm}(i)|^2 \right)^{3/2}} + +# If ``average`` is ``True``, the class computes a variant of this order +# parameter that performs an average over the first and second shell combined +# :cite:`Lechner_2008`. To compute this parameter, we perform a second +# averaging over the first neighbor shell of the particle to implicitly +# include information about the second neighbor shell. This averaging is +# performed by replacing the value :math:`q_{lm}(i)` in the original +# definition by :math:`\overline{q}_{lm}(i)`, the average value of +# :math:`q_{lm}(k)` over all the :math:`N_b` neighbors :math:`k` +# of particle :math:`i`, including particle :math:`i` itself: + +# .. math:: +# \overline{q}_{lm}(i) = \frac{1}{N_b} \sum \limits_{k=0}^{N_b} +# q_{lm}(k) + +# If ``weighted`` is True, the contributions of each neighbor are weighted. +# Neighbor weights :math:`w_{ij}` are defined for a +# :class:`freud.locality.NeighborList` obtained from +# :class:`freud.locality.Voronoi` or one with user-provided weights, and +# default to 1 if not otherwise provided. The formulas are modified as +# follows, replacing :math:`q_{lm}(i)` with the weighted value +# :math:`q'_{lm}(i)`: + +# .. math:: + +# q'_{lm}(i) = \frac{1}{\sum_{j=1}^{N_b} w_{ij}} +# \sum \limits_{j=1}^{N_b} w_{ij} Y_{lm}(\theta(\vec{r}_{ij}), +# \phi(\vec{r}_{ij})) + +# .. note:: +# The value of per-particle order parameter will be set to NaN for +# particles with no neighbors. We choose this value rather than setting +# the order parameter to 0 because in more complex order parameter +# calculations (such as when computing the :math:`w_l`), it is possible +# to observe a value of 0 for the per-particle order parameter even with +# a finite number of neighbors. If you would like to ignore this +# distinction, you can mask the output order parameter values using +# NumPy: :code:`numpy.nan_to_num(particle_order)`. + +# Args: +# l (unsigned int or sequence of unsigned int): +# One or more spherical harmonic quantum number l's used to compute +# the Steinhardt order parameter. +# average (bool, optional): +# Determines whether to calculate the averaged Steinhardt order +# parameter (Default value = :code:`False`). +# wl (bool, optional): +# Determines whether to use the :math:`w_l` version of the Steinhardt +# order parameter (Default value = :code:`False`). +# weighted (bool, optional): +# Determines whether to use neighbor weights in the computation of +# spherical harmonics over neighbors. If enabled and used with a +# Voronoi neighbor list, this results in the 3D Minkowski Structure +# Metrics :math:`q'_l` :cite:`Mickel2013` (Default value = +# :code:`False`). +# 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): +# 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 + +# @property +# def average(self): +# """bool: Whether the averaged Steinhardt order parameter was +# calculated.""" +# return self.thisptr.isAverage() + +# @property +# def wl(self): +# """bool: Whether the :math:`w_l` version of the Steinhardt order +# parameter was used.""" +# return self.thisptr.isWl() + +# @property +# def weighted(self): +# """bool: Whether neighbor weights were used in the computation.""" +# return self.thisptr.isWeighted() + +# @property +# def wl_normalize(self): +# return self.thisptr.isWlNormalized() + +# @property +# def l(self): # noqa: E743 +# """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()) +# return ls[0] if len(ls) == 1 else ls + +# @_Compute._computed_property +# def order(self): +# r"""float: The system wide normalization of the order parameter, +# computed by averaging the :math:`q_{lm}` values (or +# :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()) +# return order[0] if len(order) == 1 else order + +# @_Compute._computed_property +# 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 + +# @_Compute._computed_property +# def ql(self): +# """:math:`\\left(N_{particles}, N_l\\right)` :class:`numpy.ndarray`: +# :math:`q_l` Steinhardt order parameter for each particle (filled with +# :code:`nan` for particles with no neighbors). This is always available, +# 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 + +# @_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())] +# return qlm_list if len(qlm_list) > 1 else qlm_list[0] + +# def compute(self, system, neighbors=None): +# r"""Compute the order parameter. + +# Example:: + +# >>> box, points = freud.data.make_random_system(10, 100, seed=0) +# >>> ql = freud.order.Steinhardt(l=6) +# >>> ql.compute((box, points), {'r_max':3}) +# freud.order.Steinhardt(l=6, ...) + +# Args: +# system: +# Any object that is a valid argument to +# :class:`freud.locality.NeighborQuery.from_system`. +# neighbors (:class:`freud.locality.NeighborList` or dict, optional): +# Either a :class:`NeighborList ` of +# neighbor pairs to use in the calculation, or a dictionary of +# `query arguments +# `_ +# (Default value: None). +# """ # noqa: E501 +# cdef: +# freud.locality.NeighborQuery nq +# freud.locality.NeighborList nlist +# freud.locality._QueryArgs qargs +# const float[:, ::1] l_query_points +# unsigned int num_query_points + +# nq, nlist, qargs, l_query_points, num_query_points = \ +# self._preprocess_arguments(system, neighbors=neighbors) + +# self.thisptr.compute(nlist.get_ptr(), +# nq.get_ptr(), +# dereference(qargs.thisptr)) +# 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) + +# def plot(self, ax=None): +# """Plot order parameter distribution. + +# Args: +# ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If +# :code:`None`, make a new figure and axis +# (Default value = :code:`None`). + +# Returns: +# (:class:`matplotlib.axes.Axes`): Axis with the plot. +# """ +# import freud.plot + +# ls = self.l +# if not isinstance(ls, list): +# ls = [ls] + +# 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 '') +# for sph_l in ls +# ] +# xlabel = ', '.join(legend_labels) + +# # Don't print legend if only one l requested. +# if len(legend_labels) == 1: +# legend_labels = None + +# return freud.plot.histogram_plot( +# self.particle_order, +# title="Steinhardt Order Parameter " + xlabel, +# xlabel=xlabel, +# ylabel=r"Number of particles", +# ax=ax, +# 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): +# r"""Identifies solid-like clusters using dot products of :math:`q_{lm}`. + +# The solid-liquid order parameter :cite:`Wolde:1995aa,Filion_2010` uses a +# Steinhardt-like approach to identify solid-like particles. First, a bond +# parameter :math:`q_l(i, j)` is computed for each neighbor bond. + +# If :code:`normalize_q` is true (default), the bond parameter is given by +# :math:`q_l(i, j) = \frac{\sum \limits_{m=-l}^{l} \text{Re}~q_{lm}(i) q_{lm}^*(j)} +# {\sqrt{\sum \limits_{m=-l}^{l} \lvert q_{lm}(i) \rvert^2} +# \sqrt{\sum \limits_{m=-l}^{l} \lvert q_{lm}(j) \rvert^2}}` + +# If :code:`normalize_q` is false, then the denominator of the above +# expression is left out. + +# Next, the bonds are filtered to keep only "solid-like" bonds with +# :math:`q_l(i, j)` above a cutoff value :math:`q_{threshold}`. + +# If a particle has more than :math:`S_{threshold}` solid-like bonds, then +# the particle is considered solid-like. Finally, solid-like particles are +# clustered. + +# Args: +# l (unsigned int): +# Spherical harmonic quantum number l. +# q_threshold (float): +# Value of dot product threshold when evaluating +# :math:`q_l(i, j)` to determine if a bond is solid-like. For +# :math:`l=6`, 0.7 is generally good for FCC or BCC structures +# :cite:`Filion_2010`. +# solid_threshold (unsigned int): +# Minimum required number of adjacent solid-like bonds for a particle +# to be considered solid-like for clustering. For :math:`l=6`, 6-8 +# is generally good for FCC or BCC structures. +# 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 compute(self, system, neighbors=None): +# r"""Compute the order parameter. + +# Args: +# system: +# Any object that is a valid argument to +# :class:`freud.locality.NeighborQuery.from_system`. +# neighbors (:class:`freud.locality.NeighborList` or dict, optional): +# Either a :class:`NeighborList ` of +# neighbor pairs to use in the calculation, or a dictionary of +# `query arguments +# `_ +# (Default value: None). +# """ +# 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)) +# return self + +# @property +# def l(self): # noqa: E743 +# """unsigned int: Spherical harmonic quantum number l.""" +# return self.thisptr.getL() + +# @property +# def q_threshold(self): +# """float: Value of dot product threshold.""" +# return self.thisptr.getQThreshold() + +# @property +# def solid_threshold(self): +# """float: Value of number-of-bonds threshold.""" +# return self.thisptr.getSolidThreshold() + +# @property +# def normalize_q(self): +# """bool: Whether the dot product is normalized.""" +# return self.thisptr.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) + +# @_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) + +# @_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) + +# @_Compute._computed_property +# def cluster_sizes(self): +# """:math:`(N_{clusters}, )` :class:`np.ndarray`: The sizes of all +# clusters.""" +# return np.asarray(self.thisptr.getClusterSizes()) + +# @_Compute._computed_property +# def largest_cluster_size(self): +# """unsigned int: The largest cluster size.""" +# return self.thisptr.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 + +# @_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) + +# 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) + +# def plot(self, ax=None): +# """Plot solid-like cluster distribution. + +# Args: +# ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If +# :code:`None`, make a new figure and axis +# (Default value = :code:`None`). + +# Returns: +# (: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) + +# 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): +# """Calculates a measure of total rotational autocorrelation. + +# For any calculation of rotational correlations of extended (i.e. non-point) +# particles, encoding the symmetries of these particles is crucial to +# appropriately determining correlations. For systems of anisotropic +# particles in three dimensions, representing such equivalence can be quite +# mathematically challenging. This calculation is based on the hyperspherical +# harmonics as laid out in :cite:`Karas2019`. Generalizations of spherical +# harmonics to four dimensions, hyperspherical harmonics provide a natural +# basis for periodic functions in 4 dimensions just as harmonic functions +# (sines and cosines) or spherical harmonics do in lower dimensions. The idea +# behind this calculation is to embed orientation quaternions into a +# 4-dimensional space and then use hyperspherical harmonics to find +# correlations in a symmetry-aware fashion. + +# The choice of the hyperspherical harmonic parameter :math:`l` determines +# the symmetry of the functions. The output is not a correlation function, +# but rather a scalar value that measures total system orientational +# correlation with an initial state. As such, the output can be treated as an +# order parameter measuring degrees of rotational (de)correlation. For +# analysis of a trajectory, the compute call needs to be +# done at each trajectory frame. + +# Args: +# l (int): +# Order of the hyperspherical harmonic. Must be a positive, even +# integer. +# """ +# cdef freud._order.RotationalAutocorrelation * thisptr + +# def __cinit__(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 + +# def compute(self, ref_orientations, orientations): +# """Calculates the rotational autocorrelation function for a single frame. + +# Args: +# ref_orientations ((:math:`N_{orientations}`, 4) :class:`numpy.ndarray`): +# 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) +# return self + +# @_Compute._computed_property +# def order(self): +# """float: Autocorrelation of the system.""" +# return self.thisptr.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) + +# @property +# def l(self): # noqa: E743 +# """int: The azimuthal quantum number, which defines the order of the +# hyperspherical harmonic.""" +# return self.thisptr.getL() + +# def __repr__(self): +# return "freud.order.{cls}(l={sph_l})".format(cls=type(self).__name__, +# sph_l=self.l) + + +# cdef class ContinuousCoordination(_PairCompute): +# r"""Computes the continuous local coordination number. + +# The :class:`ContinuousCoordination` class implements extensions of the Voronoi +# discrete coordination number to the real numbers. The formulas for the +# various implementations are: + +# Power: + +# .. math:: + +# CN_p = N^{2.0 - m} \sum_{i=1}^{k} +# {\left[\left(\frac{V_i}{V}\right)^{m}\right]}^{-1} + +# Log: + +# .. math:: + +# CN_{log} = \frac{-1}{\log{N}} \sum_{i=1}^{k}\log{\left(\frac{V_i}{V}\right)} + +# Exponential: + +# .. math:: + +# CN_{exp} = \sum_{i=1}^{k}\exp{\left(\frac{V_i}{V} - \frac{1}{N} \right)} + +# where :math:`k` is the number of neighbors a particle has, :math:`V_i` is +# the volume of the pyramid (or area of the triangle in 2D) whose base is the +# Voronoi polytope facet between the central particle and neighbor :math:`i` +# and whose height is half the distance vector, and :math:`V` is the +# volume/area of the Voronoi polytope. + +# Note: +# When using multiple powers, space them out to avoid high correlations. A +# minimum spacing of 2.0 is recommended with even larger values leading to +# less correlation. + +# Args: +# powers (list[float], optional): The powers to compute the continuous +# coordination number for. The default value indicates only compute +# for power 2.0. +# (Default value: None) +# compute_log (`bool`, optional): Whether to compute the log continuous +# coordination number. +# (Default value: :code:`True`) +# compute_exp (`bool`, optional): Whether to compute the exp continuous +# coordination number. +# (Default value: :code:`True`) +# """ +# cdef freud._order.ContinuousCoordination* thisptr + +# def __cinit__(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 + +# def compute(self, system=None, voronoi=None): +# r"""Calculates the local coordination number for the specified points. + +# Example:: + +# >>> import freud +# >>> box, points = freud.data.make_random_system(10, 100, seed=0) +# >>> # Compute ContinuousCoordination +# >>> coord = freud.order.ContinuousCoordination([2, 4], True) +# >>> coord.compute(system=(box, points)) +# freud.order.ContinuousCoordination(...) + +# Args: +# system (optional): +# Any object that is a valid argument to +# :class:`freud.locality.NeighborQuery.from_system`. +# (Default value: None). +# voronoi (:class:`freud.locality.Voronoi`, optional): +# A precomputed Voronoi compute object. If provided, the object is +# 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.") +# 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.") +# cpp_voronoi = voronoi +# self.thisptr.compute(cpp_voronoi.thisptr) +# return self + +# @_Compute._computed_property +# def particle_order(self): +# """(:math:`(N_{points}, N_{coord}`) :class:`numpy.ndarray`: \ +# coordination of points per query point. + +# Coordination numbers are in order of selected powers, log, and exp. +# """ +# return self.coordination + +# @_Compute._computed_property +# def coordination(self): +# """(:math:`(N_{points}, N_{coord}`) :class:`numpy.ndarray`): \ +# coordination of points per query point. + +# 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) + +# @property +# def powers(self): +# """list[float]: The powers to compute the continuous coordination number. + +# Changes to this property are not reflected when computing coordination +# numbers. +# """ +# return self.thisptr.getPowers() + +# @property +# def compute_log(self): +# """bool: Whether to compute the log continuous coordination number.""" +# return self.thisptr.getComputeLog() + +# @property +# def compute_exp(self): +# """bool: Whether to compute the exponential coordination number.""" +# return self.thisptr.getComputeExp() + +# @property +# def number_of_coordinations(self): +# """int: The number of coordination numbers computed.""" +# return self.thisptr.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, +# ) diff --git a/freud/order/export-Nematic.cc b/freud/order/export-Nematic.cc index bb08b94e8..588e137fa 100644 --- a/freud/order/export-Nematic.cc +++ b/freud/order/export-Nematic.cc @@ -2,7 +2,7 @@ #include #include #include // NOLINT(misc-include-cleaner): used implicitly - +#include // NOLINT(misc-include-cleaner): used implicitly #include // #include "BondHistogramCompute.h" @@ -27,6 +27,11 @@ void computeNematic(const std::shared_ptr& self, 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 @@ -41,7 +46,12 @@ void export_Nematic(nanobind::module_& m) { nanobind::class_(m, "Nematic") .def(nanobind::init<>()) - .def("compute", &wrap::computeNematic, nanobind::arg("orientations")); + .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 From 32972deb8a97122308a2a89fc973fe13798fa2cf Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 11 Sep 2024 13:26:08 -0400 Subject: [PATCH 14/91] Convert to numpy array --- freud/order.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freud/order.py b/freud/order.py index 4dbdaab43..257760d19 100644 --- a/freud/order.py +++ b/freud/order.py @@ -235,13 +235,13 @@ 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 self._cpp_obj.getParticleTensor() + 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 self._cpp_obj.getNematicTensor() + return self._cpp_obj.getNematicTensor().toNumpyArray() def __repr__(self): return f"freud.order.{type(self).__name__}()" From 1758f4e6ef670dc294b238542b4ae0a237818c2d Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 22 Oct 2024 16:50:00 -0400 Subject: [PATCH 15/91] Progress on RotationalAutocorrelation translation --- freud/CMakeLists.txt | 13 ++--- freud/order/RotationalAutocorrelation.cc | 19 +++---- freud/order/RotationalAutocorrelation.h | 12 ++--- .../order/export-RotationalAutocorrelation.cc | 50 +++++++++++++++++++ 4 files changed, 71 insertions(+), 23 deletions(-) create mode 100644 freud/order/export-RotationalAutocorrelation.cc diff --git a/freud/CMakeLists.txt b/freud/CMakeLists.txt index 32fe48121..6d0493297 100644 --- a/freud/CMakeLists.txt +++ b/freud/CMakeLists.txt @@ -98,8 +98,8 @@ add_library( # order/HexaticTranslational.h order/Nematic.cc order/Nematic.h - # order/RotationalAutocorrelation.cc - # order/RotationalAutocorrelation.h + order/RotationalAutocorrelation.cc + order/RotationalAutocorrelation.h # order/SolidLiquid.cc # order/SolidLiquid.h # order/Steinhardt.cc @@ -174,13 +174,11 @@ target_set_install_rpath(_locality) # PUBLIC freud TBB::tbb) target_set_install_rpath(_order) # order -nanobind_add_module( - _order order/module-order.cc order/export-Nematic.cc -) +nanobind_add_module(_order order/module-order.cc order/export-Nematic.cc + order/export-RotationalAutocorrelation.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) @@ -215,8 +213,7 @@ set(python_files interface.py plot.py util.py) -# cluster.py density.py diffraction.py environment.py interface.py -# msd.py) +# cluster.py density.py diffraction.py environment.py interface.py msd.py) copy_files_to_build("${python_files}" "freud" "*.py") diff --git a/freud/order/RotationalAutocorrelation.cc b/freud/order/RotationalAutocorrelation.cc index cf48be4e9..c48fa4a91 100644 --- a/freud/order/RotationalAutocorrelation.cc +++ b/freud/order/RotationalAutocorrelation.cc @@ -5,6 +5,7 @@ #include "utils.h" #include +// quat imported from VectorMath in rotationalautocorrelation.h /*! \file RotationalAutocorrelation.cc \brief Implements the RotationalAutocorrelation class. @@ -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..910a1a4dd 100644 --- a/freud/order/RotationalAutocorrelation.h +++ b/freud/order/RotationalAutocorrelation.h @@ -54,11 +54,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 +72,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 +120,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/export-RotationalAutocorrelation.cc b/freud/order/export-RotationalAutocorrelation.cc new file mode 100644 index 000000000..14340de19 --- /dev/null +++ b/freud/order/export-RotationalAutocorrelation.cc @@ -0,0 +1,50 @@ +// 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 "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) +{ + // TODO: raise error on python level if orientations and ref_orientations dont match N + 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("getNematicOrderParameter", &Nematic::getNematicOrderParameter) + // .def("getNematicDirector",&wrap::getNematicDirector) + ; +} + +} // namespace detail + +}; }; // namespace freud::order From 5592e25834c4c3c771d713978810ad069a3a1eb2 Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 22 Oct 2024 21:45:47 -0400 Subject: [PATCH 16/91] Update python side of RotationalAutocorrelation --- freud/order.py | 146 ++++++++++++++++++++++--------------------------- 1 file changed, 66 insertions(+), 80 deletions(-) diff --git a/freud/order.py b/freud/order.py index 257760d19..79a696758 100644 --- a/freud/order.py +++ b/freud/order.py @@ -887,92 +887,78 @@ def __repr__(self): # return None -# cdef class RotationalAutocorrelation(_Compute): -# """Calculates a measure of total rotational autocorrelation. - -# For any calculation of rotational correlations of extended (i.e. non-point) -# particles, encoding the symmetries of these particles is crucial to -# appropriately determining correlations. For systems of anisotropic -# particles in three dimensions, representing such equivalence can be quite -# mathematically challenging. This calculation is based on the hyperspherical -# harmonics as laid out in :cite:`Karas2019`. Generalizations of spherical -# harmonics to four dimensions, hyperspherical harmonics provide a natural -# basis for periodic functions in 4 dimensions just as harmonic functions -# (sines and cosines) or spherical harmonics do in lower dimensions. The idea -# behind this calculation is to embed orientation quaternions into a -# 4-dimensional space and then use hyperspherical harmonics to find -# correlations in a symmetry-aware fashion. - -# The choice of the hyperspherical harmonic parameter :math:`l` determines -# the symmetry of the functions. The output is not a correlation function, -# but rather a scalar value that measures total system orientational -# correlation with an initial state. As such, the output can be treated as an -# order parameter measuring degrees of rotational (de)correlation. For -# analysis of a trajectory, the compute call needs to be -# done at each trajectory frame. - -# Args: -# l (int): -# Order of the hyperspherical harmonic. Must be a positive, even -# integer. -# """ -# cdef freud._order.RotationalAutocorrelation * thisptr - -# def __cinit__(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 - -# def compute(self, ref_orientations, orientations): -# """Calculates the rotational autocorrelation function for a single frame. - -# Args: -# ref_orientations ((:math:`N_{orientations}`, 4) :class:`numpy.ndarray`): -# 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) +class RotationalAutocorrelation(_Compute): + """Calculates a measure of total rotational autocorrelation. + + For any calculation of rotational correlations of extended (i.e. non-point) + particles, encoding the symmetries of these particles is crucial to + appropriately determining correlations. For systems of anisotropic + particles in three dimensions, representing such equivalence can be quite + mathematically challenging. This calculation is based on the hyperspherical + harmonics as laid out in :cite:`Karas2019`. Generalizations of spherical + harmonics to four dimensions, hyperspherical harmonics provide a natural + basis for periodic functions in 4 dimensions just as harmonic functions + (sines and cosines) or spherical harmonics do in lower dimensions. The idea + behind this calculation is to embed orientation quaternions into a + 4-dimensional space and then use hyperspherical harmonics to find + correlations in a symmetry-aware fashion. + + The choice of the hyperspherical harmonic parameter :math:`l` determines + the symmetry of the functions. The output is not a correlation function, + but rather a scalar value that measures total system orientational + correlation with an initial state. As such, the output can be treated as an + order parameter measuring degrees of rotational (de)correlation. For + analysis of a trajectory, the compute call needs to be + done at each trajectory frame. + + Args: + l (int): + Order of the hyperspherical harmonic. Must be a positive, even + integer. + """ -# cdef const float[:, ::1] l_ref_orientations = ref_orientations -# cdef const float[:, ::1] l_orientations = orientations -# cdef unsigned int nP = orientations.shape[0] + def __init__(self, l): # noqa: E741 + if l % 2 or l < 0: + raise ValueError("The quantum number must be a positive, even integer.") # noqa: EM101 + self._cpp_obj = freud._order.RotationalAutocorrelation(l) -# self.thisptr.compute( -# &l_ref_orientations[0, 0], -# &l_orientations[0, 0], -# nP) -# return self + def compute(self, ref_orientations, orientations): + """Calculates the rotational autocorrelation function for a single frame. -# @_Compute._computed_property -# def order(self): -# """float: Autocorrelation of the system.""" -# return self.thisptr.getRotationalAutocorrelation() + Args: + ref_orientations ((:math:`N_{orientations}`, 4) :class:`numpy.ndarray`): + Orientations for the initial frame. + orientations ((:math:`N_{orientations}`, 4) :class:`numpy.ndarray`): + Orientations for the frame of interest. + """ + 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 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) + @_Compute._computed_property + def order(self): + """float: Autocorrelation of the system.""" + return self._cpp_obj.getRotationalAutocorrelation() -# @property -# def l(self): # noqa: E743 -# """int: The azimuthal quantum number, which defines the order of the -# hyperspherical harmonic.""" -# return self.thisptr.getL() + @_Compute._computed_property + def particle_order(self): + """(:math:`N_{orientations}`) :class:`numpy.ndarray`: Rotational + autocorrelation values calculated for each orientation.""" + return self._cpp_obj.getRAArray().toNumpyArray() + # freud.util.make_managed_numpy_array( + # &self.thisptr.getRAArray(), + # freud.util.arr_type_t.COMPLEX_FLOAT) + + @property + def l(self): # noqa: E743 + """int: The azimuthal quantum number, which defines the order of the + hyperspherical harmonic.""" + return self._cpp_obj.getL() -# def __repr__(self): -# return "freud.order.{cls}(l={sph_l})".format(cls=type(self).__name__, -# sph_l=self.l) + def __repr__(self): + return f"freud.order.{type(self).__name__}(l={self.l})" # cdef class ContinuousCoordination(_PairCompute): From b0900b723e87adcdf55ec2e8708c1d2b093f712d Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 22 Oct 2024 21:48:24 -0400 Subject: [PATCH 17/91] Export RotationalAutocorrelation --- freud/order/export-RotationalAutocorrelation.cc | 16 +++++++++++----- freud/order/module-order.cc | 11 +++++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/freud/order/export-RotationalAutocorrelation.cc b/freud/order/export-RotationalAutocorrelation.cc index 14340de19..d15054dc8 100644 --- a/freud/order/export-RotationalAutocorrelation.cc +++ b/freud/order/export-RotationalAutocorrelation.cc @@ -11,6 +11,8 @@ #include "RotationalAutocorrelation.h" #include "VectorMath.h" +namespace nb = nanobind; + namespace freud { namespace order { template @@ -22,7 +24,6 @@ void computeRotationalAutocorrelation(const std::shared_ptr>& ref_orientations, const nb_array>& orientations) { - // TODO: raise error on python level if orientations and ref_orientations dont match N unsigned int const num_orientations = orientations.shape(0); auto* ref_orientations_data = reinterpret_cast*>(ref_orientations.data()); auto* orientations_data = reinterpret_cast*>(orientations.data()); @@ -30,6 +31,11 @@ void computeRotationalAutocorrelation(const std::shared_ptrcompute(ref_orientations_data, orientations_data, num_orientations); } +void getRAArray(const std::shared_ptr& self) +{ + auto raarray = self->getRAArray(); // TODO: convert to std::vector then nb list? +} + }; // namespace wrap namespace detail { @@ -37,12 +43,12 @@ namespace detail { void export_RotationalAutocorrelation(nanobind::module_& m) { nanobind::class_(m, "RotationalAutocorrelation") - .def(nanobind::init<>()) + .def(nanobind::init()) .def("compute", &wrap::computeRotationalAutocorrelation, nanobind::arg("ref_orientations"), nanobind::arg("orientations")) - // .def("getNematicOrderParameter", &Nematic::getNematicOrderParameter) - // .def("getNematicDirector",&wrap::getNematicDirector) - ; + .def("getL", &RotationalAutocorrelation::getL) + .def("getRotationalAutocorrelation", &RotationalAutocorrelation::getRotationalAutocorrelation) + .def("getRAArray", &wrap::getRAArray); } } // namespace detail diff --git a/freud/order/module-order.cc b/freud/order/module-order.cc index b6fc0356b..ad9d3912d 100644 --- a/freud/order/module-order.cc +++ b/freud/order/module-order.cc @@ -1,15 +1,18 @@ +// 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_PMFT(nanobind::module_& m); - // void export_PMFTXY(nanobind::module_& m); - void export_Nematic(nanobind::module_& m); -} // namespace freud::pmft::detail +void export_Nematic(nanobind::module_& m); +void export_RotationalAutocorrelation(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); } From a4e7a824430d131abcb8916556d8c0d167d4f352 Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 22 Oct 2024 21:51:31 -0400 Subject: [PATCH 18/91] Include missing header --- freud/order/RotationalAutocorrelation.h | 1 + 1 file changed, 1 insertion(+) diff --git a/freud/order/RotationalAutocorrelation.h b/freud/order/RotationalAutocorrelation.h index 910a1a4dd..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" From 31942250c0fc3bf04632a9ba00854641c664b4e5 Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 22 Oct 2024 16:12:40 -0400 Subject: [PATCH 19/91] WIP on Steinhardt --- doc/source/gettingstarted/examples | 2 +- freud/CMakeLists.txt | 8 +++--- freud/order.py | 31 ++++++++++++---------- freud/order/Nematic.cc | 4 +-- freud/order/Nematic.h | 11 ++++---- freud/order/Steinhardt.cc | 7 ++--- freud/order/export-Nematic.cc | 27 ++++++++++---------- freud/order/export-Steinhardt.cc | 41 ++++++++++++++++++++++++++++++ freud/order/module-order.cc | 2 ++ 9 files changed, 91 insertions(+), 42 deletions(-) create mode 100644 freud/order/export-Steinhardt.cc diff --git a/doc/source/gettingstarted/examples b/doc/source/gettingstarted/examples index 18c84895a..b135ea692 160000 --- a/doc/source/gettingstarted/examples +++ b/doc/source/gettingstarted/examples @@ -1 +1 @@ -Subproject commit 18c84895abf64ed5766957d8aa20322c3f18364b +Subproject commit b135ea69247308bd0a46f6a880f8c1944bf9a5f1 diff --git a/freud/CMakeLists.txt b/freud/CMakeLists.txt index 6d0493297..11aa63f68 100644 --- a/freud/CMakeLists.txt +++ b/freud/CMakeLists.txt @@ -102,10 +102,10 @@ add_library( order/RotationalAutocorrelation.h # order/SolidLiquid.cc # order/SolidLiquid.h - # order/Steinhardt.cc - # order/Steinhardt.h - # order/Wigner3j.cc - # order/Wigner3j.h + order/Steinhardt.cc + order/Steinhardt.h + order/Wigner3j.cc + order/Wigner3j.h # parallel parallel/tbb_config.h parallel/tbb_config.cc diff --git a/freud/order.py b/freud/order.py index 79a696758..cd0b24187 100644 --- a/freud/order.py +++ b/freud/order.py @@ -10,7 +10,7 @@ Fourier Transforms. """ -from freud.util import _Compute #, quat, vec3 +from freud.util import _Compute # , quat, vec3 from freud.errors import FreudDeprecationWarning @@ -185,10 +185,10 @@ class Nematic(_Compute): freud.order.Nematic() """ + def __init__(self): self._cpp_obj = freud._order.Nematic() - def compute(self, orientations): r"""Calculates the per-particle and global order parameter. @@ -204,17 +204,20 @@ def compute(self, orientations): Args: orientations (:math:`\left(N_{particles}, 3 \right)` :class:`numpy.ndarray`): Orientation vectors for which to calculate the order parameter. - """ # noqa: E501 + """ # 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) + 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, + ) self._cpp_obj.compute(orientations) return self @@ -233,8 +236,8 @@ def director(self): @_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.""" + One 3x3 matrix per-particle corresponding to each individual + particle orientation.""" return self._cpp_obj.getParticleTensor().toNumpyArray() @_Compute._computed_property diff --git a/freud/order/Nematic.cc b/freud/order/Nematic.cc index 4303792d8..d4923cfd9 100644 --- a/freud/order/Nematic.cc +++ b/freud/order/Nematic.cc @@ -43,7 +43,7 @@ vec3 Nematic::getNematicDirector() const void Nematic::compute(vec3* orientations, unsigned int n) { m_n = n; - m_particle_tensor = std::make_shared>(std::vector {m_n, 3, 3} ); + m_particle_tensor = std::make_shared>(std::vector {m_n, 3, 3}); m_nematic_tensor_local->reset(); // calculate per-particle tensor @@ -79,7 +79,7 @@ void Nematic::compute(vec3* orientations, unsigned int n) }); // Now calculate the sum of Q_ab's - m_nematic_tensor = std::make_shared>(std::vector {3,3}); + m_nematic_tensor = std::make_shared>(std::vector {3, 3}); m_nematic_tensor_local->reduceInto(*m_nematic_tensor); // Normalize by the number of particles diff --git a/freud/order/Nematic.h b/freud/order/Nematic.h index 8d80e695c..55463c69e 100644 --- a/freud/order/Nematic.h +++ b/freud/order/Nematic.h @@ -23,11 +23,11 @@ class Nematic { public: //! Constructor - Nematic() { - m_nematic_tensor = std::make_shared>(std::vector {3,3} ); - m_nematic_tensor_local = std::make_shared>( std::vector {3,3} ); + 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; @@ -53,7 +53,8 @@ class Nematic 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. + 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/Steinhardt.cc b/freud/order/Steinhardt.cc index 6d7350e5d..d3b815139 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,14 +41,14 @@ void Steinhardt::reallocateArrays(unsigned int Np) const auto num_ls = m_ls.size(); - m_qli.prepare({Np, num_ls}); + m_qli = std::make_shared> {Np, num_ls}; if (m_average) { - m_qliAve.prepare({Np, num_ls}); + m_qliAve = std::make_shared> {Np, num_ls}; } if (m_wl) { - m_wli.prepare({Np, num_ls}); + m_wli = std::make_shared> {Np, num_ls}; } for (size_t l_index = 0; l_index < m_ls.size(); ++l_index) diff --git a/freud/order/export-Nematic.cc b/freud/order/export-Nematic.cc index 588e137fa..24c9d9770 100644 --- a/freud/order/export-Nematic.cc +++ b/freud/order/export-Nematic.cc @@ -1,8 +1,11 @@ +// 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 // NOLINT(misc-include-cleaner): used implicitly #include // #include "BondHistogramCompute.h" @@ -18,13 +21,13 @@ using nb_array = nanobind::ndarray& self, - const nb_array>& orientations) +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); + 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) @@ -34,7 +37,6 @@ nanobind::tuple getNematicDirector(const std::shared_ptr& self) } }; // namespace wrap - namespace detail { // void export_PMFT(nanobind::module_& m) @@ -48,12 +50,11 @@ void export_Nematic(nanobind::module_& m) .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) - ; + .def("getNematicDirector", &wrap::getNematicDirector) + .def("getParticleTensor", &Nematic::getParticleTensor) + .def("getNematicTensor", &Nematic::getNematicTensor); } } // namespace detail -}; }; // end namespace freud::pmft +}; }; // namespace freud::order diff --git a/freud/order/export-Steinhardt.cc b/freud/order/export-Steinhardt.cc new file mode 100644 index 000000000..bf6ec48df --- /dev/null +++ b/freud/order/export-Steinhardt.cc @@ -0,0 +1,41 @@ +// 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 "Steinhardt.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); +// } + +}; // namespace wrap + +namespace detail { + +void export_Steinhardt(nanobind::module_& m) +{ + nanobind::class_(m, "Steinhardt").def(nanobind::init<>()); +} + +} // namespace detail + +}; }; // namespace freud::order diff --git a/freud/order/module-order.cc b/freud/order/module-order.cc index ad9d3912d..d1ed687ab 100644 --- a/freud/order/module-order.cc +++ b/freud/order/module-order.cc @@ -7,6 +7,7 @@ namespace freud::order::detail { void export_Nematic(nanobind::module_& m); void export_RotationalAutocorrelation(nanobind::module_& m); +void export_Steinhardt(nanobind::module_& m); } // namespace freud::order::detail using namespace freud::order::detail; @@ -15,4 +16,5 @@ NB_MODULE(_order, module) // NOLINT(misc-use-anonymous-namespace): caused by nan { export_Nematic(module); export_RotationalAutocorrelation(module); + export_Steinhardt(module); } From c85fdde6b7a58a87a65c62028035f096293789c9 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 13:28:19 -0400 Subject: [PATCH 20/91] 17 errors --- freud/order/Steinhardt.cc | 8 ++++---- freud/order/Steinhardt.h | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index d3b815139..57ee5b569 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -41,14 +41,14 @@ void Steinhardt::reallocateArrays(unsigned int Np) const auto num_ls = m_ls.size(); - m_qli = std::make_shared> {Np, num_ls}; + m_qli = std::shared_ptr>(std::vector {Np, num_ls}); if (m_average) { - m_qliAve = std::make_shared> {Np, num_ls}; + m_qliAve = std::shared_ptr>(std::vector{Np, num_ls}); } if (m_wl) { - m_wli = std::make_shared> {Np, num_ls}; + m_wli = std::shared_ptr>(std::vector{Np, num_ls}); } for (size_t l_index = 0; l_index < m_ls.size(); ++l_index) @@ -240,7 +240,7 @@ 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]; diff --git a/freud/order/Steinhardt.h b/freud/order/Steinhardt.h index 7d6f56fa3..a3f5b4608 100644 --- a/freud/order/Steinhardt.h +++ b/freud/order/Steinhardt.h @@ -94,7 +94,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 +104,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) { @@ -205,14 +205,14 @@ class Steinhardt 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::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>> 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 }; From 728f08eb53efc13ea35dd711c5957470559463bb Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 13:43:11 -0400 Subject: [PATCH 21/91] 6 errors --- freud/order/Steinhardt.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index 57ee5b569..ab6154627 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -41,14 +41,14 @@ void Steinhardt::reallocateArrays(unsigned int Np) const auto num_ls = m_ls.size(); - m_qli = std::shared_ptr>(std::vector {Np, num_ls}); + m_qli = std::make_shared>(std::vector{Np, num_ls}); if (m_average) { - m_qliAve = std::shared_ptr>(std::vector{Np, num_ls}); + m_qliAve = std::make_shared>(std::vector{Np, num_ls}); } if (m_wl) { - m_wli = std::shared_ptr>(std::vector{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) @@ -171,7 +171,7 @@ 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 @@ -187,15 +187,15 @@ void Steinhardt::baseCompute(const freud::locality::NeighborList* nlist, 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); } } - 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]); } }); } @@ -258,10 +258,10 @@ void Steinhardt::computeAve(const freud::locality::NeighborList* nlist, 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]); } }); } From 1cf81334f7dd5172fbdf7b4e19cec92ac7e5468a Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 14:01:15 -0400 Subject: [PATCH 22/91] 5 errors --- freud/order/Steinhardt.cc | 10 +++++++--- freud/order/Steinhardt.h | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index ab6154627..f39da896b 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -54,11 +54,14 @@ void Steinhardt::reallocateArrays(unsigned int Np) 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].prepare({Np, num_ms}); + m_qlmi[l_index] = std::make_shared>>(std::vector{Np, num_ms}); + // m_qlm[l_index].prepare(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].prepare({Np, num_ms}); + m_qlmAve[l_index] = std::make_shared>>(std::vector{Np, num_ms}); } } } @@ -305,6 +308,7 @@ std::vector Steinhardt::normalizeSystem() } void Steinhardt::aggregatewl(util::ManagedArray& target, +// void Steinhardt::aggregatewl(std::shared_ptr> target, const std::vector>>& source, const util::ManagedArray& normalization_source) const { diff --git a/freud/order/Steinhardt.h b/freud/order/Steinhardt.h index a3f5b4608..333c8ea48 100644 --- a/freud/order/Steinhardt.h +++ b/freud/order/Steinhardt.h @@ -202,6 +202,7 @@ class Steinhardt 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_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 @@ -209,7 +210,7 @@ class Steinhardt 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 std::shared_ptr> From b19d52164bb7749badbf95370b88d4ccb9ff9fcb Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 14:07:03 -0400 Subject: [PATCH 23/91] 17 errors --- freud/order/Steinhardt.cc | 8 ++++---- freud/order/Steinhardt.h | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index f39da896b..265de24ce 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -257,11 +257,11 @@ 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]); diff --git a/freud/order/Steinhardt.h b/freud/order/Steinhardt.h index 333c8ea48..6efd73fd5 100644 --- a/freud/order/Steinhardt.h +++ b/freud/order/Steinhardt.h @@ -6,6 +6,7 @@ #include #include +#include #include "Box.h" #include "ManagedArray.h" @@ -201,14 +202,14 @@ 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_qlmi; //!< qlm for each particle i // std::vector>>> m_qlmi; //!< qlm for each particle i - std::vector>> m_qlm; //!< Normalized qlm(Ave) for the whole system + std::vector>>> m_qlm; //!< Normalized qlm(Ave) for the whole system 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>> + std::vector>>> m_qlmiAve; //!< Averaged qlm with 2nd neighbor shell for each particle i std::vector>>> m_qlmAve; //!< Normalized qlmiAve for the whole system From 356efee54a4edaec6a819247f2355f8f1d06c13f Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 14:08:20 -0400 Subject: [PATCH 24/91] 15 --- freud/order/Steinhardt.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index 265de24ce..7a731ca27 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -166,7 +166,7 @@ void Steinhardt::baseCompute(const freud::locality::NeighborList* nlist, 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 @@ -188,7 +188,7 @@ 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]); // This array gets populated by computeAve in the averaging case. From 5ec3279a472f5ec25b406633c282eb348af86818 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 15:06:21 -0400 Subject: [PATCH 25/91] 10 errors --- freud/order/Steinhardt.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index 7a731ca27..daef81ae1 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -190,11 +190,11 @@ void Steinhardt::baseCompute(const freud::locality::NeighborList* nlist, (*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]; @@ -235,7 +235,7 @@ void Steinhardt::computeAve(const freud::locality::NeighborList* nlist, // 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++; @@ -281,7 +281,7 @@ 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); From 4a82ec94f79fe0726147dbf2cf6fcf0101feae7e Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 15:08:38 -0400 Subject: [PATCH 26/91] 5 errors --- freud/order/Steinhardt.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index daef81ae1..a59c0ec04 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -163,7 +163,7 @@ 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]; @@ -180,7 +180,7 @@ void Steinhardt::baseCompute(const freud::locality::NeighborList* nlist, // 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) @@ -228,8 +228,8 @@ 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 @@ -249,7 +249,7 @@ void Steinhardt::computeAve(const freud::locality::NeighborList* nlist, 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) From be22bf8bb969eb5a170b76d21c0761874fe944c8 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 15:12:22 -0400 Subject: [PATCH 27/91] 3 errors --- freud/order/Steinhardt.cc | 2 +- freud/order/Steinhardt.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index a59c0ec04..90a510354 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -83,7 +83,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) diff --git a/freud/order/Steinhardt.h b/freud/order/Steinhardt.h index 6efd73fd5..0542218c0 100644 --- a/freud/order/Steinhardt.h +++ b/freud/order/Steinhardt.h @@ -115,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; } From 0193019521e44c8fac19ad46ed993383734fc404 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 15:17:13 -0400 Subject: [PATCH 28/91] 2 errors --- freud/order/Steinhardt.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freud/order/Steinhardt.h b/freud/order/Steinhardt.h index 0542218c0..93a533bce 100644 --- a/freud/order/Steinhardt.h +++ b/freud/order/Steinhardt.h @@ -187,9 +187,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 From bff43f0deed4a1039050332759f8bacc9eee8943 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 15:30:35 -0400 Subject: [PATCH 29/91] 1 error --- freud/order/Steinhardt.cc | 3 ++- freud/order/Wigner3j.cc | 2 ++ freud/order/Wigner3j.h | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index 90a510354..a34d3fcee 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -288,7 +288,8 @@ std::vector Steinhardt::normalizeSystem() 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); + // float wl_system_norm = reduceWigner3j(qlm.get(), l, wigner3j_values); // The normalization factor of wl is calculated using qli, which is // equivalent to calculate the normalization factor from qlmi diff --git a/freud/order/Wigner3j.cc b/freud/order/Wigner3j.cc index ea3674433..eb860b023 100644 --- a/freud/order/Wigner3j.cc +++ b/freud/order/Wigner3j.cc @@ -6,6 +6,7 @@ #include "Wigner3j.h" + /*! \file Wigner3j.cc * \brief Stores and reduces over Wigner 3j coefficients for l from 0 to 20 */ @@ -18,6 +19,7 @@ inline int lmIndex(int l, int m) } float reduceWigner3j(const std::complex* source, unsigned int l_, const std::vector& wigner3j) +// float reduceWigner3j(const util::ManagedArray>* source, unsigned int l_, const std::vector& wigner3j) { /* * Wigner 3j coefficients: diff --git a/freud/order/Wigner3j.h b/freud/order/Wigner3j.h index 1618a535f..ce247f4c7 100644 --- a/freud/order/Wigner3j.h +++ b/freud/order/Wigner3j.h @@ -7,6 +7,9 @@ #include #include +#include "VectorMath.h" +#include "ManagedArray.h" + /*! \file Wigner3j.h * \brief Stores and reduces over Wigner 3j coefficients for l from 0 to 20 */ @@ -21,6 +24,7 @@ int lmIndex(int l, int m); // third-order rotational invariant quantity. // source array must be indexed by m, like [0, 1, ..., l, -1, -2, ..., -l]. float reduceWigner3j(const std::complex* source, unsigned int l_, const std::vector& wigner3j); +// float reduceWigner3j(const util::ManagedArray>* source, unsigned int l_, const std::vector& wigner3j); std::vector getWigner3j(unsigned int l); // All Wigner 3j coefficients created using sympy From aa0ffb1ef2512425b3a65732b07660565051fa95 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 15:38:02 -0400 Subject: [PATCH 30/91] 3 errors --- freud/order/Steinhardt.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index a34d3fcee..f26a55f65 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -308,16 +308,16 @@ std::vector Steinhardt::normalizeSystem() return system_norms; } -void Steinhardt::aggregatewl(util::ManagedArray& target, -// void Steinhardt::aggregatewl(std::shared_ptr> target, - const std::vector>>& source, - const util::ManagedArray& normalization_source) const +// void Steinhardt::aggregatewl(util::ManagedArray& target, +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]; @@ -326,13 +326,13 @@ 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] + (*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; + (*target)[target_particle_index + l_index] *= normalization * normalization * normalization; } } } From 9872c90529435fcc71ee0a1deb374eb3cf1c13cc Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 15:41:15 -0400 Subject: [PATCH 31/91] 0 errors --- freud/order/Steinhardt.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index f26a55f65..9c03a86cf 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -327,11 +327,11 @@ void Steinhardt::aggregatewl(std::shared_ptr>& target, const auto wigner3j_values = getWigner3j(l); (*target)[target_particle_index + l_index] - = reduceWigner3j(&(source_l({i, 0})), l, wigner3j_values); + = 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]; + / (*normalization_source)[norm_particle_index + l_index]; (*target)[target_particle_index + l_index] *= normalization * normalization * normalization; } } From 391dad039abab6fe2215a7c378e78529491043fe Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 15:44:33 -0400 Subject: [PATCH 32/91] Fix Steinhardt __init__ --- freud/CMakeLists.txt | 3 ++- freud/order/export-Steinhardt.cc | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/freud/CMakeLists.txt b/freud/CMakeLists.txt index 11aa63f68..c8e66a9a2 100644 --- a/freud/CMakeLists.txt +++ b/freud/CMakeLists.txt @@ -175,7 +175,8 @@ target_set_install_rpath(_locality) # order nanobind_add_module(_order order/module-order.cc order/export-Nematic.cc - order/export-RotationalAutocorrelation.cc) + order/export-RotationalAutocorrelation.cc + order/export-Steinhardt.cc) target_link_libraries(_order PUBLIC freud) target_set_install_rpath(_order) diff --git a/freud/order/export-Steinhardt.cc b/freud/order/export-Steinhardt.cc index bf6ec48df..694a0191a 100644 --- a/freud/order/export-Steinhardt.cc +++ b/freud/order/export-Steinhardt.cc @@ -33,7 +33,8 @@ namespace detail { void export_Steinhardt(nanobind::module_& m) { - nanobind::class_(m, "Steinhardt").def(nanobind::init<>()); + nanobind::class_(m, "Steinhardt").def(nanobind::init()) + ; } } // namespace detail From 264d749d42413250bfbec6c25a44dcb119c69320 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 16:05:48 -0400 Subject: [PATCH 33/91] First property --- freud/order.py | 255 +++++++++++++++---------------- freud/order/export-Steinhardt.cc | 10 +- 2 files changed, 130 insertions(+), 135 deletions(-) diff --git a/freud/order.py b/freud/order.py index cd0b24187..f6a213771 100644 --- a/freud/order.py +++ b/freud/order.py @@ -16,7 +16,7 @@ # from cython.operator cimport dereference -# from freud.locality cimport _PairCompute +from freud.locality import _PairCompute import collections.abc import logging @@ -405,132 +405,127 @@ def __repr__(self): # return None -# cdef 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`. +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`. + + Implements the local rotationally invariant :math:`q_l` or :math:`w_l` + order parameter described by Steinhardt. + + First, we describe the computation of :math:`q_l(i)`. For a particle :math:`i`, + we calculate the quantity :math:`q_{lm}` by summing the spherical harmonics + between particle :math:`i` and its neighbors :math:`j` in a local region: + + .. math:: + + q_{lm}(i) = \frac{1}{N_b} \sum \limits_{j=1}^{N_b} + Y_{lm}(\theta(\vec{r}_{ij}), \phi(\vec{r}_{ij})) + + Then the :math:`q_l` order parameter is computed by combining the :math:`q_{lm}` + in a rotationally invariant fashion to remove local orientational order: + + .. math:: + + q_l(i) = \sqrt{\frac{4\pi}{2l+1} \sum \limits_{m=-l}^{l} + |q_{lm}(i)|^2 } + + If the ``wl`` parameter is ``True``, this class computes the quantity + :math:`w_l`, defined as a weighted average over the + :math:`q_{lm}(i)` values using `Wigner 3-j symbols + `__ (related to `Clebsch-Gordan + coefficients + `__). + The resulting combination is rotationally invariant: + + .. math:: + + w_l(i) = \sum \limits_{m_1 + m_2 + m_3 = 0} \begin{pmatrix} + l & l & l \\ + m_1 & m_2 & m_3 + \end{pmatrix} + q_{lm_1}(i) q_{lm_2}(i) q_{lm_3}(i) + + If ``wl`` is ``True``, then setting the ``wl_normalize`` parameter to ``True`` will + normalize the :math:`w_l` order parameter as follows (if ``wl=False``, + ``wl_normalize`` has no effect): + + .. math:: + + w_l(i) = \frac{ + \sum \limits_{m_1 + m_2 + m_3 = 0} \begin{pmatrix} + l & l & l \\ + m_1 & m_2 & m_3 + \end{pmatrix} + q_{lm_1}(i) q_{lm_2}(i) q_{lm_3}(i)} + {\left(\sum \limits_{m=-l}^{l} |q_{lm}(i)|^2 \right)^{3/2}} + + If ``average`` is ``True``, the class computes a variant of this order + parameter that performs an average over the first and second shell combined + :cite:`Lechner_2008`. To compute this parameter, we perform a second + averaging over the first neighbor shell of the particle to implicitly + include information about the second neighbor shell. This averaging is + performed by replacing the value :math:`q_{lm}(i)` in the original + definition by :math:`\overline{q}_{lm}(i)`, the average value of + :math:`q_{lm}(k)` over all the :math:`N_b` neighbors :math:`k` + of particle :math:`i`, including particle :math:`i` itself: + + .. math:: + \overline{q}_{lm}(i) = \frac{1}{N_b} \sum \limits_{k=0}^{N_b} + q_{lm}(k) + + If ``weighted`` is True, the contributions of each neighbor are weighted. + Neighbor weights :math:`w_{ij}` are defined for a + :class:`freud.locality.NeighborList` obtained from + :class:`freud.locality.Voronoi` or one with user-provided weights, and + default to 1 if not otherwise provided. The formulas are modified as + follows, replacing :math:`q_{lm}(i)` with the weighted value + :math:`q'_{lm}(i)`: + + .. math:: + + q'_{lm}(i) = \frac{1}{\sum_{j=1}^{N_b} w_{ij}} + \sum \limits_{j=1}^{N_b} w_{ij} Y_{lm}(\theta(\vec{r}_{ij}), + \phi(\vec{r}_{ij})) + + .. note:: + The value of per-particle order parameter will be set to NaN for + particles with no neighbors. We choose this value rather than setting + the order parameter to 0 because in more complex order parameter + calculations (such as when computing the :math:`w_l`), it is possible + to observe a value of 0 for the per-particle order parameter even with + a finite number of neighbors. If you would like to ignore this + distinction, you can mask the output order parameter values using + NumPy: :code:`numpy.nan_to_num(particle_order)`. -# Implements the local rotationally invariant :math:`q_l` or :math:`w_l` -# order parameter described by Steinhardt. - -# First, we describe the computation of :math:`q_l(i)`. For a particle :math:`i`, -# we calculate the quantity :math:`q_{lm}` by summing the spherical harmonics -# between particle :math:`i` and its neighbors :math:`j` in a local region: - -# .. math:: - -# q_{lm}(i) = \frac{1}{N_b} \sum \limits_{j=1}^{N_b} -# Y_{lm}(\theta(\vec{r}_{ij}), \phi(\vec{r}_{ij})) - -# Then the :math:`q_l` order parameter is computed by combining the :math:`q_{lm}` -# in a rotationally invariant fashion to remove local orientational order: - -# .. math:: - -# q_l(i) = \sqrt{\frac{4\pi}{2l+1} \sum \limits_{m=-l}^{l} -# |q_{lm}(i)|^2 } - -# If the ``wl`` parameter is ``True``, this class computes the quantity -# :math:`w_l`, defined as a weighted average over the -# :math:`q_{lm}(i)` values using `Wigner 3-j symbols -# `__ (related to `Clebsch-Gordan -# coefficients -# `__). -# The resulting combination is rotationally invariant: - -# .. math:: - -# w_l(i) = \sum \limits_{m_1 + m_2 + m_3 = 0} \begin{pmatrix} -# l & l & l \\ -# m_1 & m_2 & m_3 -# \end{pmatrix} -# q_{lm_1}(i) q_{lm_2}(i) q_{lm_3}(i) - -# If ``wl`` is ``True``, then setting the ``wl_normalize`` parameter to ``True`` will -# normalize the :math:`w_l` order parameter as follows (if ``wl=False``, -# ``wl_normalize`` has no effect): - -# .. math:: - -# w_l(i) = \frac{ -# \sum \limits_{m_1 + m_2 + m_3 = 0} \begin{pmatrix} -# l & l & l \\ -# m_1 & m_2 & m_3 -# \end{pmatrix} -# q_{lm_1}(i) q_{lm_2}(i) q_{lm_3}(i)} -# {\left(\sum \limits_{m=-l}^{l} |q_{lm}(i)|^2 \right)^{3/2}} - -# If ``average`` is ``True``, the class computes a variant of this order -# parameter that performs an average over the first and second shell combined -# :cite:`Lechner_2008`. To compute this parameter, we perform a second -# averaging over the first neighbor shell of the particle to implicitly -# include information about the second neighbor shell. This averaging is -# performed by replacing the value :math:`q_{lm}(i)` in the original -# definition by :math:`\overline{q}_{lm}(i)`, the average value of -# :math:`q_{lm}(k)` over all the :math:`N_b` neighbors :math:`k` -# of particle :math:`i`, including particle :math:`i` itself: - -# .. math:: -# \overline{q}_{lm}(i) = \frac{1}{N_b} \sum \limits_{k=0}^{N_b} -# q_{lm}(k) - -# If ``weighted`` is True, the contributions of each neighbor are weighted. -# Neighbor weights :math:`w_{ij}` are defined for a -# :class:`freud.locality.NeighborList` obtained from -# :class:`freud.locality.Voronoi` or one with user-provided weights, and -# default to 1 if not otherwise provided. The formulas are modified as -# follows, replacing :math:`q_{lm}(i)` with the weighted value -# :math:`q'_{lm}(i)`: - -# .. math:: - -# q'_{lm}(i) = \frac{1}{\sum_{j=1}^{N_b} w_{ij}} -# \sum \limits_{j=1}^{N_b} w_{ij} Y_{lm}(\theta(\vec{r}_{ij}), -# \phi(\vec{r}_{ij})) - -# .. note:: -# The value of per-particle order parameter will be set to NaN for -# particles with no neighbors. We choose this value rather than setting -# the order parameter to 0 because in more complex order parameter -# calculations (such as when computing the :math:`w_l`), it is possible -# to observe a value of 0 for the per-particle order parameter even with -# a finite number of neighbors. If you would like to ignore this -# distinction, you can mask the output order parameter values using -# NumPy: :code:`numpy.nan_to_num(particle_order)`. - -# Args: -# l (unsigned int or sequence of unsigned int): -# One or more spherical harmonic quantum number l's used to compute -# the Steinhardt order parameter. -# average (bool, optional): -# Determines whether to calculate the averaged Steinhardt order -# parameter (Default value = :code:`False`). -# wl (bool, optional): -# Determines whether to use the :math:`w_l` version of the Steinhardt -# order parameter (Default value = :code:`False`). -# weighted (bool, optional): -# Determines whether to use neighbor weights in the computation of -# spherical harmonics over neighbors. If enabled and used with a -# Voronoi neighbor list, this results in the 3D Minkowski Structure -# Metrics :math:`q'_l` :cite:`Mickel2013` (Default value = -# :code:`False`). -# 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 + Args: + l (unsigned int or sequence of unsigned int): + One or more spherical harmonic quantum number l's used to compute + the Steinhardt order parameter. + average (bool, optional): + Determines whether to calculate the averaged Steinhardt order + parameter (Default value = :code:`False`). + wl (bool, optional): + Determines whether to use the :math:`w_l` version of the Steinhardt + order parameter (Default value = :code:`False`). + weighted (bool, optional): + Determines whether to use neighbor weights in the computation of + spherical harmonics over neighbors. If enabled and used with a + Voronoi neighbor list, this results in the 3D Minkowski Structure + Metrics :math:`q'_l` :cite:`Mickel2013` (Default value = + :code:`False`). + wl_normalize (bool, optional): + Determines whether to normalize the :math:`w_l` version + of the Steinhardt order parameter (Default value = :code:`False`). + """ -# def __cinit__(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 __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._cpp_obj = freud._order.Steinhardt(l, average, wl, weighted, wl_normalize) -# def __dealloc__(self): -# del self.thisptr # @property # def average(self): @@ -553,13 +548,11 @@ def __repr__(self): # def wl_normalize(self): # return self.thisptr.isWlNormalized() -# @property -# def l(self): # noqa: E743 -# """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()) -# return ls[0] if len(ls) == 1 else ls + @property + def l(self): # noqa: E743 + """unsigned int: Spherical harmonic quantum number l.""" + ls = self._cpp_obj.getL() + return ls[0] if len(ls) == 1 else ls # @_Compute._computed_property # def order(self): diff --git a/freud/order/export-Steinhardt.cc b/freud/order/export-Steinhardt.cc index 694a0191a..2fb3ef281 100644 --- a/freud/order/export-Steinhardt.cc +++ b/freud/order/export-Steinhardt.cc @@ -4,8 +4,8 @@ #include #include #include -#include // NOLINT(misc-include-cleaner): used implicitly -#include // NOLINT(misc-include-cleaner): used implicitly +// #include // NOLINT(misc-include-cleaner): used implicitly +#include #include #include "Steinhardt.h" @@ -33,8 +33,10 @@ namespace detail { void export_Steinhardt(nanobind::module_& m) { - nanobind::class_(m, "Steinhardt").def(nanobind::init()) - ; + nanobind::class_(m, "Steinhardt") + .def(nanobind::init, bool, bool, bool, bool>()) + .def("getL", &Steinhardt::getL) + ; } } // namespace detail From 19a0006cc6a598a3bc253159530c42a04eeb49cb Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 16:14:11 -0400 Subject: [PATCH 34/91] Scalar properties --- freud/order.py | 61 +++++++++++++++----------------- freud/order/export-Steinhardt.cc | 4 +++ 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/freud/order.py b/freud/order.py index f6a213771..f1985b64a 100644 --- a/freud/order.py +++ b/freud/order.py @@ -1,6 +1,6 @@ # 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. - +# ruff: noqa: E731 r""" The :class:`freud.order` module contains functions which compute order parameters for the whole system or individual particles. Order parameters take @@ -527,26 +527,26 @@ def __init__(self, l, average=False, wl=False, weighted=False, wl_normalize=Fals 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() + @property + def average(self): + """bool: Whether the averaged Steinhardt order parameter was + calculated.""" + 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() + @property + def wl(self): + """bool: Whether the :math:`w_l` version of the Steinhardt order + parameter was used.""" + return self._cpp_obj.isWl() -# @property -# def weighted(self): -# """bool: Whether neighbor weights were used in the computation.""" -# return self.thisptr.isWeighted() + @property + def weighted(self): + """bool: Whether neighbor weights were used in the computation.""" + return self._cpp_obj.isWeighted() -# @property -# def wl_normalize(self): -# return self.thisptr.isWlNormalized() + @property + def wl_normalize(self): + return self._cpp_obj.isWlNormalized() @property def l(self): # noqa: E743 @@ -640,15 +640,10 @@ def l(self): # noqa: E743 # dereference(qargs.thisptr)) # 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) + def __repr__(self): + 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. @@ -689,12 +684,12 @@ def l(self): # noqa: E743 # ax=ax, # 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 + 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): diff --git a/freud/order/export-Steinhardt.cc b/freud/order/export-Steinhardt.cc index 2fb3ef281..6ab0c0eff 100644 --- a/freud/order/export-Steinhardt.cc +++ b/freud/order/export-Steinhardt.cc @@ -35,6 +35,10 @@ void export_Steinhardt(nanobind::module_& m) { nanobind::class_(m, "Steinhardt") .def(nanobind::init, bool, bool, bool, bool>()) + .def("isAverage", &Steinhardt::isAverage) + .def("isWl", &Steinhardt::isWl) + .def("isWeighted", &Steinhardt::isWeighted) + .def("isWlNormalized", &Steinhardt::isWlNormalized) .def("getL", &Steinhardt::getL) ; } From 77b31a831648ab151cc3514f461b89f9c1998e6d Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 23 Oct 2024 16:27:31 -0400 Subject: [PATCH 35/91] Initial compute interface --- freud/order.py | 52 ++++++++++++++------------------ freud/order/export-Steinhardt.cc | 1 + 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/freud/order.py b/freud/order.py index f1985b64a..c77b8360c 100644 --- a/freud/order.py +++ b/freud/order.py @@ -604,41 +604,33 @@ def l(self): # noqa: E743 # for i in range(qlm_arrays.size())] # return qlm_list if len(qlm_list) > 1 else qlm_list[0] -# def compute(self, system, neighbors=None): -# r"""Compute the order parameter. + def compute(self, system, neighbors=None): + r"""Compute the order parameter. -# Example:: + Example:: -# >>> box, points = freud.data.make_random_system(10, 100, seed=0) -# >>> ql = freud.order.Steinhardt(l=6) -# >>> ql.compute((box, points), {'r_max':3}) -# freud.order.Steinhardt(l=6, ...) + >>> box, points = freud.data.make_random_system(10, 100, seed=0) + >>> ql = freud.order.Steinhardt(l=6) + >>> ql.compute((box, points), {'r_max':3}) + freud.order.Steinhardt(l=6, ...) -# Args: -# system: -# Any object that is a valid argument to -# :class:`freud.locality.NeighborQuery.from_system`. -# neighbors (:class:`freud.locality.NeighborList` or dict, optional): -# Either a :class:`NeighborList ` of -# neighbor pairs to use in the calculation, or a dictionary of -# `query arguments -# `_ -# (Default value: None). -# """ # noqa: E501 -# cdef: -# freud.locality.NeighborQuery nq -# freud.locality.NeighborList nlist -# freud.locality._QueryArgs qargs -# const float[:, ::1] l_query_points -# unsigned int num_query_points + Args: + system: + Any object that is a valid argument to + :class:`freud.locality.NeighborQuery.from_system`. + neighbors (:class:`freud.locality.NeighborList` or dict, optional): + Either a :class:`NeighborList ` of + neighbor pairs to use in the calculation, or a dictionary of + `query arguments + `_ + (Default value: None). + """ -# nq, nlist, qargs, l_query_points, num_query_points = \ -# self._preprocess_arguments(system, neighbors=neighbors) + # Call the pair compute setup function + 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)) -# return self + self._cpp_obj.compute(nlist, nq, qargs._cpp_obj) + return self def __repr__(self): return (f"freud.order.{type(self).__name__}(l={self.l}, " diff --git a/freud/order/export-Steinhardt.cc b/freud/order/export-Steinhardt.cc index 6ab0c0eff..52f237a47 100644 --- a/freud/order/export-Steinhardt.cc +++ b/freud/order/export-Steinhardt.cc @@ -35,6 +35,7 @@ void export_Steinhardt(nanobind::module_& m) { nanobind::class_(m, "Steinhardt") .def(nanobind::init, bool, bool, bool, bool>()) + .def("compute", &Steinhardt::compute) // TODO: may require preprocessing? .def("isAverage", &Steinhardt::isAverage) .def("isWl", &Steinhardt::isWl) .def("isWeighted", &Steinhardt::isWeighted) From 21cdd7a137604d6fed4d52d60a50a5f00442406d Mon Sep 17 00:00:00 2001 From: janbridley Date: Thu, 24 Oct 2024 10:55:15 -0400 Subject: [PATCH 36/91] Fix final method in RotationalAutocorrelation --- freud/order/export-RotationalAutocorrelation.cc | 8 ++------ freud/util/export-ManagedArray.cc | 2 ++ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/freud/order/export-RotationalAutocorrelation.cc b/freud/order/export-RotationalAutocorrelation.cc index d15054dc8..090b0e4de 100644 --- a/freud/order/export-RotationalAutocorrelation.cc +++ b/freud/order/export-RotationalAutocorrelation.cc @@ -8,6 +8,7 @@ #include // NOLINT(misc-include-cleaner): used implicitly #include +#include "ManagedArray.h" #include "RotationalAutocorrelation.h" #include "VectorMath.h" @@ -31,11 +32,6 @@ void computeRotationalAutocorrelation(const std::shared_ptrcompute(ref_orientations_data, orientations_data, num_orientations); } -void getRAArray(const std::shared_ptr& self) -{ - auto raarray = self->getRAArray(); // TODO: convert to std::vector then nb list? -} - }; // namespace wrap namespace detail { @@ -48,7 +44,7 @@ void export_RotationalAutocorrelation(nanobind::module_& m) nanobind::arg("orientations")) .def("getL", &RotationalAutocorrelation::getL) .def("getRotationalAutocorrelation", &RotationalAutocorrelation::getRotationalAutocorrelation) - .def("getRAArray", &wrap::getRAArray); + .def("getRAArray", &RotationalAutocorrelation::getRAArray); } } // namespace detail diff --git a/freud/util/export-ManagedArray.cc b/freud/util/export-ManagedArray.cc index 7e2a03e35..a9d420bea 100644 --- a/freud/util/export-ManagedArray.cc +++ b/freud/util/export-ManagedArray.cc @@ -3,6 +3,7 @@ #include #include // NOLINT(misc-include-cleaner): used implicitly +#include #include "VectorMath.h" #include "export-ManagedArray.h" @@ -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 From 3352f46db31293f35eb90fbc4291352d633c8298 Mon Sep 17 00:00:00 2001 From: janbridley Date: Thu, 24 Oct 2024 11:53:52 -0400 Subject: [PATCH 37/91] Steinhardt shared_ptr to neighbor queries --- freud/order.py | 6 ++---- freud/order/Steinhardt.cc | 14 ++++++++------ freud/order/Steinhardt.h | 13 +++++++------ 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/freud/order.py b/freud/order.py index c77b8360c..a7f79af31 100644 --- a/freud/order.py +++ b/freud/order.py @@ -627,9 +627,10 @@ def compute(self, system, neighbors=None): """ # Call the pair compute setup function + print(neighbors) nq, nlist, qargs, l_query_points, num_query_points = self._preprocess_arguments(system, neighbors=neighbors) - self._cpp_obj.compute(nlist, nq, qargs._cpp_obj) + self._cpp_obj.compute(nlist._cpp_obj, nq, qargs._cpp_obj) return self def __repr__(self): @@ -930,9 +931,6 @@ def particle_order(self): """(:math:`N_{orientations}`) :class:`numpy.ndarray`: Rotational autocorrelation values calculated for each orientation.""" return self._cpp_obj.getRAArray().toNumpyArray() - # freud.util.make_managed_numpy_array( - # &self.thisptr.getRAArray(), - # freud.util.arr_type_t.COMPLEX_FLOAT) @property def l(self): # noqa: E743 diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index 9c03a86cf..d7d18dc3e 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -66,8 +66,9 @@ void Steinhardt::reallocateArrays(unsigned int Np) } } -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()); @@ -100,8 +101,8 @@ 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) @@ -203,8 +204,9 @@ void Steinhardt::baseCompute(const freud::locality::NeighborList* nlist, }); } -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) diff --git a/freud/order/Steinhardt.h b/freud/order/Steinhardt.h index 93a533bce..d8d210374 100644 --- a/freud/order/Steinhardt.h +++ b/freud/order/Steinhardt.h @@ -151,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 { @@ -174,12 +175,12 @@ 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. From 3a6ea38e329ff64ce1e7fabb77983bfbb29c4a79 Mon Sep 17 00:00:00 2001 From: janbridley Date: Thu, 24 Oct 2024 14:42:11 -0400 Subject: [PATCH 38/91] Update NeighborComputeFunctional to use shared pointers --- freud/locality/NeighborComputeFunctional.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/freud/locality/NeighborComputeFunctional.h b/freud/locality/NeighborComputeFunctional.h index 1c45725e7..88bf90e62 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,7 +76,7 @@ class NeighborListPerPointIterator : public NeighborPerPointIterator } private: - const NeighborList* m_nlist; //! The NeighborList being iterated over. + 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 @@ -110,8 +110,8 @@ 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, +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 @@ -122,8 +122,7 @@ void loopOverNeighborsIterator(const NeighborQuery* neighbor_query, const vec3 niter - = std::make_shared(nlist, i); + NeighborListPerPointIterator niter = NeighborListPerPointIterator(nlist, i); cf(i, niter); } }, From 522e3ecbb556274865ec572cfca86c97ca6ef3de Mon Sep 17 00:00:00 2001 From: "Joshua A. Anderson" Date: Thu, 24 Oct 2024 16:12:50 -0400 Subject: [PATCH 39/91] Fix compile error. --- freud/locality/NeighborComputeFunctional.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freud/locality/NeighborComputeFunctional.h b/freud/locality/NeighborComputeFunctional.h index 88bf90e62..8a3d78eec 100644 --- a/freud/locality/NeighborComputeFunctional.h +++ b/freud/locality/NeighborComputeFunctional.h @@ -122,7 +122,8 @@ void loopOverNeighborsIterator(const std::shared_ptr& neighbor_qu [&](size_t begin, size_t end) { for (size_t i = begin; i != end; ++i) { - NeighborListPerPointIterator niter = NeighborListPerPointIterator(nlist, i); + std::shared_ptr niter + = std::make_shared(nlist, i); cf(i, niter); } }, From 4d009cb687f372d89eb8608236372822d47f1afd Mon Sep 17 00:00:00 2001 From: "Joshua A. Anderson" Date: Thu, 24 Oct 2024 16:49:34 -0400 Subject: [PATCH 40/91] compute() call completes. --- freud/order.py | 3 +-- freud/order/export-Steinhardt.cc | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/freud/order.py b/freud/order.py index a7f79af31..78f1b13bf 100644 --- a/freud/order.py +++ b/freud/order.py @@ -627,10 +627,9 @@ def compute(self, system, neighbors=None): """ # Call the pair compute setup function - print(neighbors) nq, nlist, qargs, l_query_points, num_query_points = self._preprocess_arguments(system, neighbors=neighbors) - self._cpp_obj.compute(nlist._cpp_obj, nq, qargs._cpp_obj) + self._cpp_obj.compute(nlist._cpp_obj, nq._cpp_obj, qargs._cpp_obj) return self def __repr__(self): diff --git a/freud/order/export-Steinhardt.cc b/freud/order/export-Steinhardt.cc index 52f237a47..db932db57 100644 --- a/freud/order/export-Steinhardt.cc +++ b/freud/order/export-Steinhardt.cc @@ -6,6 +6,8 @@ #include // #include // NOLINT(misc-include-cleaner): used implicitly #include +#include // NOLINT(misc-include-cleaner): used implicitly + #include #include "Steinhardt.h" @@ -35,7 +37,7 @@ void export_Steinhardt(nanobind::module_& m) { nanobind::class_(m, "Steinhardt") .def(nanobind::init, bool, bool, bool, bool>()) - .def("compute", &Steinhardt::compute) // TODO: may require preprocessing? + .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) From 7833b4c576fddbfde74728342cebce6971163b34 Mon Sep 17 00:00:00 2001 From: janbridley Date: Fri, 25 Oct 2024 13:27:27 -0400 Subject: [PATCH 41/91] Add qlm and particle order exports --- freud/order.py | 38 ++++++++++++++------------------ freud/order/export-Steinhardt.cc | 2 ++ 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/freud/order.py b/freud/order.py index 78f1b13bf..3a72fbcca 100644 --- a/freud/order.py +++ b/freud/order.py @@ -566,16 +566,15 @@ def l(self): # noqa: E743 # order = list(self.thisptr.getOrder()) # return order[0] if len(order) == 1 else order -# @_Compute._computed_property -# 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 + @_Compute._computed_property + 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).""" + particle_orders = self._cpp_obj.getParticleOrder().toNumpyArray() + if particle_orders.shape[1] == 1: + return np.ravel(particle_orders) + return particle_orders # @_Compute._computed_property # def ql(self): @@ -591,18 +590,13 @@ def l(self): # noqa: E743 # return np.ravel(array) # return array -# @_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())] -# return qlm_list if len(qlm_list) > 1 else qlm_list[0] + @_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_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): r"""Compute the order parameter. diff --git a/freud/order/export-Steinhardt.cc b/freud/order/export-Steinhardt.cc index db932db57..0b45afa47 100644 --- a/freud/order/export-Steinhardt.cc +++ b/freud/order/export-Steinhardt.cc @@ -43,6 +43,8 @@ void export_Steinhardt(nanobind::module_& m) .def("isWeighted", &Steinhardt::isWeighted) .def("isWlNormalized", &Steinhardt::isWlNormalized) .def("getL", &Steinhardt::getL) + .def("getParticleOrder", &Steinhardt::getParticleOrder) + .def("getQlm", &Steinhardt::getQlm) ; } From fcfd1d923fedf5cbfc5a9b1548414e26a9670e4c Mon Sep 17 00:00:00 2001 From: janbridley Date: Fri, 25 Oct 2024 14:52:42 -0400 Subject: [PATCH 42/91] Fix segfalt when computing averaged steinhardt --- freud/order.py | 23 ++++++++++++----------- freud/order/Steinhardt.cc | 2 +- freud/order/export-Steinhardt.cc | 1 + 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/freud/order.py b/freud/order.py index 3a72fbcca..c3fc3a7bd 100644 --- a/freud/order.py +++ b/freud/order.py @@ -554,17 +554,17 @@ def l(self): # noqa: E743 ls = self._cpp_obj.getL() return ls[0] if len(ls) == 1 else ls -# @_Compute._computed_property -# def order(self): -# r"""float: The system wide normalization of the order parameter, -# computed by averaging the :math:`q_{lm}` values (or -# :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()) -# return order[0] if len(order) == 1 else order + @_Compute._computed_property + def order(self): + r"""float: The system wide normalization of the order parameter, + computed by averaging the :math:`q_{lm}` values (or + :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 = self._cpp_obj.getOrder() + return order[0] if len(order) == 1 else order @_Compute._computed_property def particle_order(self): @@ -621,6 +621,7 @@ def compute(self, system, neighbors=None): """ # Call the pair compute setup function + # breakpoint() 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) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index d7d18dc3e..604659e4b 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -61,7 +61,7 @@ void Steinhardt::reallocateArrays(unsigned int Np) if (m_average) { // m_qlmiAve[l_index].prepare({Np, num_ms}); - m_qlmAve[l_index] = std::make_shared>>(std::vector{Np, num_ms}); + m_qlmiAve[l_index] = std::make_shared>>(std::vector{Np, num_ms}); } } } diff --git a/freud/order/export-Steinhardt.cc b/freud/order/export-Steinhardt.cc index 0b45afa47..c201e5d81 100644 --- a/freud/order/export-Steinhardt.cc +++ b/freud/order/export-Steinhardt.cc @@ -43,6 +43,7 @@ void export_Steinhardt(nanobind::module_& m) .def("isWeighted", &Steinhardt::isWeighted) .def("isWlNormalized", &Steinhardt::isWlNormalized) .def("getL", &Steinhardt::getL) + .def("getOrder", &Steinhardt::getOrder) .def("getParticleOrder", &Steinhardt::getParticleOrder) .def("getQlm", &Steinhardt::getQlm) ; From a9c8cd890b71cf71d9a89261d88f09494fdf24fc Mon Sep 17 00:00:00 2001 From: janbridley Date: Fri, 25 Oct 2024 14:57:45 -0400 Subject: [PATCH 43/91] export ql --- freud/order.py | 29 +++++++++++++++-------------- freud/order/export-Steinhardt.cc | 1 + 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/freud/order.py b/freud/order.py index c3fc3a7bd..c2d805cb6 100644 --- a/freud/order.py +++ b/freud/order.py @@ -576,19 +576,21 @@ def particle_order(self): return np.ravel(particle_orders) return particle_orders -# @_Compute._computed_property -# def ql(self): -# """:math:`\\left(N_{particles}, N_l\\right)` :class:`numpy.ndarray`: -# :math:`q_l` Steinhardt order parameter for each particle (filled with -# :code:`nan` for particles with no neighbors). This is always available, -# 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 + @_Compute._computed_property + def ql(self): + """:math:`\\left(N_{particles}, N_l\\right)` :class:`numpy.ndarray`: + :math:`q_l` Steinhardt order parameter for each particle (filled with + :code:`nan` for particles with no neighbors). This is always available, + 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``.""" + ql = self._cpp_obj.getQl().toNumpyArray() + return np.ravel(ql) if ql.shape[1] == 1 else ql + # 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 ql @_Compute._computed_property def particle_harmonics(self): @@ -621,7 +623,6 @@ def compute(self, system, neighbors=None): """ # Call the pair compute setup function - # breakpoint() 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) diff --git a/freud/order/export-Steinhardt.cc b/freud/order/export-Steinhardt.cc index c201e5d81..fbf06f545 100644 --- a/freud/order/export-Steinhardt.cc +++ b/freud/order/export-Steinhardt.cc @@ -46,6 +46,7 @@ void export_Steinhardt(nanobind::module_& m) .def("getOrder", &Steinhardt::getOrder) .def("getParticleOrder", &Steinhardt::getParticleOrder) .def("getQlm", &Steinhardt::getQlm) + .def("getQl", &Steinhardt::getQl) ; } From 6c472fc1ba172ef5f0f5179832ffbddbbb3bbe4d Mon Sep 17 00:00:00 2001 From: janbridley Date: Fri, 25 Oct 2024 15:03:23 -0400 Subject: [PATCH 44/91] Clean up order.py for steinhardt --- freud/order.py | 87 ++++++++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 49 deletions(-) diff --git a/freud/order.py b/freud/order.py index c2d805cb6..844d51f32 100644 --- a/freud/order.py +++ b/freud/order.py @@ -1,6 +1,6 @@ # 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. -# ruff: noqa: E731 +# ruff: noqa: E741 r""" The :class:`freud.order` module contains functions which compute order parameters for the whole system or individual particles. Order parameters take @@ -523,7 +523,7 @@ def __init__(self, l, average=False, wl=False, weighted=False, wl_normalize=Fals if not isinstance(l, collections.abc.Sequence): l = [l] if len(l) == 0: - raise ValueError("At least one l must be specified.") + raise ValueError("At least one l must be specified.") # noqa: EM101 self._cpp_obj = freud._order.Steinhardt(l, average, wl, weighted, wl_normalize) @@ -561,8 +561,6 @@ 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 = self._cpp_obj.getOrder() return order[0] if len(order) == 1 else order @@ -571,10 +569,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).""" - particle_orders = self._cpp_obj.getParticleOrder().toNumpyArray() - if particle_orders.shape[1] == 1: - return np.ravel(particle_orders) - return particle_orders + 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): @@ -586,11 +582,6 @@ def ql(self): ``average``, ``wl``, ``wl_normalize``.""" ql = self._cpp_obj.getQl().toNumpyArray() return np.ravel(ql) if ql.shape[1] == 1 else ql - # 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 ql @_Compute._computed_property def particle_harmonics(self): @@ -633,44 +624,42 @@ def __repr__(self): 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. - -# Args: -# ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If -# :code:`None`, make a new figure and axis -# (Default value = :code:`None`). + def plot(self, ax=None): + """Plot order parameter distribution. -# Returns: -# (:class:`matplotlib.axes.Axes`): Axis with the plot. -# """ -# import freud.plot - -# ls = self.l -# if not isinstance(ls, list): -# ls = [ls] - -# 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 '') -# for sph_l in ls -# ] -# xlabel = ', '.join(legend_labels) - -# # Don't print legend if only one l requested. -# if len(legend_labels) == 1: -# legend_labels = None + Args: + ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If + :code:`None`, make a new figure and axis + (Default value = :code:`None`). -# return freud.plot.histogram_plot( -# self.particle_order, -# title="Steinhardt Order Parameter " + xlabel, -# xlabel=xlabel, -# ylabel=r"Number of particles", -# ax=ax, -# legend_labels=legend_labels) + Returns: + (:class:`matplotlib.axes.Axes`): Axis with the plot. + """ + import freud.plot + + ls = self.l + if not isinstance(ls, list): + ls = [ls] + + legend_labels = [ + ( + fr"${'w' if self.wl else 'q'}{'\'' if self.weighted else ''}_" + f"{{{sph_l}{',ave' if self.average else ''}}}$" + ) for sph_l in ls + ] + xlabel = ', '.join(legend_labels) + + # Don't print legend if only one l requested. + if len(legend_labels) == 1: + legend_labels = None + + return freud.plot.histogram_plot( + self.particle_order, + title="Steinhardt Order Parameter " + xlabel, + xlabel=xlabel, + ylabel=r"Number of particles", + ax=ax, + legend_labels=legend_labels) def _repr_png_(self): try: From 51fdb5aef7cbeb3439849475e9357c9bdc3400a5 Mon Sep 17 00:00:00 2001 From: janbridley Date: Fri, 25 Oct 2024 15:43:29 -0400 Subject: [PATCH 45/91] SolidLiquid progress --- freud/CMakeLists.txt | 8 ++++---- freud/order/SolidLiquid.cc | 27 +++++++++++++++----------- freud/order/SolidLiquid.h | 16 ++++++++-------- freud/order/export-SolidLiquid.cc | 32 +++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 23 deletions(-) create mode 100644 freud/order/export-SolidLiquid.cc diff --git a/freud/CMakeLists.txt b/freud/CMakeLists.txt index c8e66a9a2..90f30095b 100644 --- a/freud/CMakeLists.txt +++ b/freud/CMakeLists.txt @@ -100,8 +100,8 @@ add_library( order/Nematic.h order/RotationalAutocorrelation.cc order/RotationalAutocorrelation.h - # order/SolidLiquid.cc - # order/SolidLiquid.h + order/SolidLiquid.cc + order/SolidLiquid.h order/Steinhardt.cc order/Steinhardt.h order/Wigner3j.cc @@ -175,8 +175,8 @@ target_set_install_rpath(_locality) # order nanobind_add_module(_order order/module-order.cc order/export-Nematic.cc - order/export-RotationalAutocorrelation.cc - order/export-Steinhardt.cc) + order/export-RotationalAutocorrelation.cc order/export-Steinhardt.cc + order/export-SolidLiquid.cc) target_link_libraries(_order PUBLIC freud) target_set_install_rpath(_order) diff --git a/freud/order/SolidLiquid.cc b/freud/order/SolidLiquid.cc index 330f4d19a..354e24847 100644 --- a/freud/order/SolidLiquid.cc +++ b/freud/order/SolidLiquid.cc @@ -19,8 +19,10 @@ 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) +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); @@ -36,7 +38,8 @@ void SolidLiquid::compute(const freud::locality::NeighborList* nlist, // 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); + // m_ql_ij.prepare(num_bonds); + m_ql_ij = std::make_shared>(std::vector {num_bonds}); util::forLoopWrapper( 0, num_query_points, @@ -59,9 +62,9 @@ void SolidLiquid::compute(const freud::locality::NeighborList* nlist, // 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 +74,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); 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.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 @@ -91,14 +96,14 @@ void SolidLiquid::compute(const freud::locality::NeighborList* nlist, { 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); + 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); 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, solid_neighbor_nlist, qargs); } }; }; // end namespace freud::order diff --git a/freud/order/SolidLiquid.h b/freud/order/SolidLiquid.h index 65e080e70..d9d3b4529 100644 --- a/freud/order/SolidLiquid.h +++ b/freud/order/SolidLiquid.h @@ -85,8 +85,8 @@ 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 +107,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; } @@ -130,13 +130,13 @@ class SolidLiquid } //! 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; } @@ -152,8 +152,8 @@ class SolidLiquid 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 + 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 }; diff --git a/freud/order/export-SolidLiquid.cc b/freud/order/export-SolidLiquid.cc new file mode 100644 index 000000000..74020355e --- /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 +#include // NOLINT(misc-include-cleaner): used implicitly +// #include // NOLINT(misc-include-cleaner): used implicitly +#include + +// #include "ManagedArray.h" +#include "SolidLiquid.h" +// #include "VectorMath.h" + + +namespace freud { namespace order { + +namespace wrap {}; // namespace wrap + +namespace detail { + +void export_SolidLiquid(nanobind::module_& m) +{ + nanobind::class_(m, "SolidLiquid") + .def(nanobind::init()) + // .def("getRAArray", &RotationalAutocorrelation::getRAArray) + ; +} + +} // namespace detail + +}; }; // namespace freud::order From 80ecb1e71a3ad7d28a8ed7e2ed1f305762aa714a Mon Sep 17 00:00:00 2001 From: janbridley Date: Fri, 25 Oct 2024 15:48:06 -0400 Subject: [PATCH 46/91] 2 errors --- CMakeLists.txt | 11 +++++++++++ freud/order/SolidLiquid.cc | 15 ++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7614f5d8e..beeb1206a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,17 @@ else() set(_using_conda Off) endif() +# Enable diagnostic colors for ninja +if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.0) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdiagnostics-color=always") +endif() + +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics") +endif() + include_directories( ${PROJECT_SOURCE_DIR}/freud/box ${PROJECT_SOURCE_DIR}/freud/cluster diff --git a/freud/order/SolidLiquid.cc b/freud/order/SolidLiquid.cc index 354e24847..94a1cf670 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 { @@ -47,15 +48,15 @@ void SolidLiquid::compute(const std::shared_ptr& 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) + 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, @@ -94,16 +95,16 @@ void SolidLiquid::compute(const std::shared_ptr& 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)); + 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 From 744c0162849a0cd2a427e1d83166272f9840dadc Mon Sep 17 00:00:00 2001 From: janbridley Date: Fri, 25 Oct 2024 15:56:49 -0400 Subject: [PATCH 47/91] 1 error --- freud/order/SolidLiquid.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freud/order/SolidLiquid.cc b/freud/order/SolidLiquid.cc index 94a1cf670..8a5f94b67 100644 --- a/freud/order/SolidLiquid.cc +++ b/freud/order/SolidLiquid.cc @@ -31,7 +31,7 @@ void SolidLiquid::compute(const std::shared_ptr& 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(std::make_shared(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(); From 5a12795b8d0834432c77f696a9d8b3a5a2eba7bf Mon Sep 17 00:00:00 2001 From: janbridley Date: Fri, 25 Oct 2024 16:01:35 -0400 Subject: [PATCH 48/91] Compile SolidLiquid --- freud/order/SolidLiquid.cc | 14 +++++++------- freud/order/SolidLiquid.h | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/freud/order/SolidLiquid.cc b/freud/order/SolidLiquid.cc index 8a5f94b67..caff3e203 100644 --- a/freud/order/SolidLiquid.cc +++ b/freud/order/SolidLiquid.cc @@ -28,17 +28,17 @@ void SolidLiquid::compute(const std::shared_ptr& // 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(std::make_shared(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()); + const unsigned int num_bonds(m_nlist->getNumBonds()); // m_ql_ij.prepare(num_bonds); m_ql_ij = std::make_shared>(std::vector {num_bonds}); @@ -47,10 +47,10 @@ void SolidLiquid::compute(const std::shared_ptr& [&](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; @@ -77,7 +77,7 @@ void SolidLiquid::compute(const std::shared_ptr& { 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 diff --git a/freud/order/SolidLiquid.h b/freud/order/SolidLiquid.h index d9d3b4529..eba8d6fbf 100644 --- a/freud/order/SolidLiquid.h +++ b/freud/order/SolidLiquid.h @@ -124,9 +124,9 @@ 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 @@ -147,7 +147,7 @@ class SolidLiquid 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. + 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 From 5c0ddce9f1a3c09f1bbb4e294254083c7ecdece3 Mon Sep 17 00:00:00 2001 From: janbridley Date: Fri, 25 Oct 2024 16:07:18 -0400 Subject: [PATCH 49/91] clean up SolidLiquid --- freud/order/SolidLiquid.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/freud/order/SolidLiquid.cc b/freud/order/SolidLiquid.cc index caff3e203..41de5a922 100644 --- a/freud/order/SolidLiquid.cc +++ b/freud/order/SolidLiquid.cc @@ -39,7 +39,6 @@ void SolidLiquid::compute(const std::shared_ptr& // 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); m_ql_ij = std::make_shared>(std::vector {num_bonds}); util::forLoopWrapper( @@ -81,7 +80,6 @@ void SolidLiquid::compute(const std::shared_ptr& 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++) From 39a9e147a1fc1bca3a04171e1c336150238bc0a7 Mon Sep 17 00:00:00 2001 From: janbridley Date: Fri, 25 Oct 2024 16:07:22 -0400 Subject: [PATCH 50/91] export SolidLiquid --- freud/order/module-order.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freud/order/module-order.cc b/freud/order/module-order.cc index d1ed687ab..dc20b4756 100644 --- a/freud/order/module-order.cc +++ b/freud/order/module-order.cc @@ -8,6 +8,7 @@ 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); } // namespace freud::order::detail using namespace freud::order::detail; @@ -17,4 +18,5 @@ NB_MODULE(_order, module) // NOLINT(misc-use-anonymous-namespace): caused by nan export_Nematic(module); export_RotationalAutocorrelation(module); export_Steinhardt(module); + export_SolidLiquid(module); } From 7b66c2b3df81abd9bd37f8cf02584a522cbc7b0b Mon Sep 17 00:00:00 2001 From: janbridley Date: Fri, 25 Oct 2024 16:14:30 -0400 Subject: [PATCH 51/91] Initial SolidLiquid python layer --- freud/order.py | 116 ++++++++++++++---------------- freud/order/export-SolidLiquid.cc | 3 +- freud/order/export-Steinhardt.cc | 13 ---- 3 files changed, 53 insertions(+), 79 deletions(-) diff --git a/freud/order.py b/freud/order.py index 844d51f32..e0ecbf46b 100644 --- a/freud/order.py +++ b/freud/order.py @@ -1,5 +1,5 @@ # 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. +# This file is from the freud project, released under the BSD 3-Clause License-> # ruff: noqa: E741 r""" The :class:`freud.order` module contains functions which compute order @@ -669,80 +669,68 @@ def _repr_png_(self): return None -# cdef class SolidLiquid(_PairCompute): -# r"""Identifies solid-like clusters using dot products of :math:`q_{lm}`. +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 -# Steinhardt-like approach to identify solid-like particles. First, a bond -# parameter :math:`q_l(i, j)` is computed for each neighbor bond. + The solid-liquid order parameter :cite:`Wolde:1995aa,Filion_2010` uses a + Steinhardt-like approach to identify solid-like particles. First, a bond + parameter :math:`q_l(i, j)` is computed for each neighbor bond. -# If :code:`normalize_q` is true (default), the bond parameter is given by -# :math:`q_l(i, j) = \frac{\sum \limits_{m=-l}^{l} \text{Re}~q_{lm}(i) q_{lm}^*(j)} -# {\sqrt{\sum \limits_{m=-l}^{l} \lvert q_{lm}(i) \rvert^2} -# \sqrt{\sum \limits_{m=-l}^{l} \lvert q_{lm}(j) \rvert^2}}` + If :code:`normalize_q` is true (default), the bond parameter is given by + :math:`q_l(i, j) = \frac{\sum \limits_{m=-l}^{l} \text{Re}~q_{lm}(i) q_{lm}^*(j)} + {\sqrt{\sum \limits_{m=-l}^{l} \lvert q_{lm}(i) \rvert^2} + \sqrt{\sum \limits_{m=-l}^{l} \lvert q_{lm}(j) \rvert^2}}` -# If :code:`normalize_q` is false, then the denominator of the above -# expression is left out. + If :code:`normalize_q` is false, then the denominator of the above + expression is left out. -# Next, the bonds are filtered to keep only "solid-like" bonds with -# :math:`q_l(i, j)` above a cutoff value :math:`q_{threshold}`. + Next, the bonds are filtered to keep only "solid-like" bonds with + :math:`q_l(i, j)` above a cutoff value :math:`q_{threshold}`. -# If a particle has more than :math:`S_{threshold}` solid-like bonds, then -# the particle is considered solid-like. Finally, solid-like particles are -# clustered. + If a particle has more than :math:`S_{threshold}` solid-like bonds, then + the particle is considered solid-like. Finally, solid-like particles are + clustered. -# Args: -# l (unsigned int): -# Spherical harmonic quantum number l. -# q_threshold (float): -# Value of dot product threshold when evaluating -# :math:`q_l(i, j)` to determine if a bond is solid-like. For -# :math:`l=6`, 0.7 is generally good for FCC or BCC structures -# :cite:`Filion_2010`. -# solid_threshold (unsigned int): -# Minimum required number of adjacent solid-like bonds for a particle -# to be considered solid-like for clustering. For :math:`l=6`, 6-8 -# is generally good for FCC or BCC structures. -# normalize_q (bool): -# Whether to normalize the dot product (Default value = -# :code:`True`). -# """ # noqa: E501 -# cdef freud._order.SolidLiquid * thisptr + Args: + l (unsigned int): + Spherical harmonic quantum number l. + q_threshold (float): + Value of dot product threshold when evaluating + :math:`q_l(i, j)` to determine if a bond is solid-like. For + :math:`l=6`, 0.7 is generally good for FCC or BCC structures + :cite:`Filion_2010`. + solid_threshold (unsigned int): + Minimum required number of adjacent solid-like bonds for a particle + to be considered solid-like for clustering. For :math:`l=6`, 6-8 + is generally good for FCC or BCC structures. + normalize_q (bool): + Whether to normalize the dot product (Default value = + :code:`True`). + """ -# 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 __init__(self, l, q_threshold, solid_threshold, normalize_q=True): + self._cpp_obj = freud._order.SolidLiquid(l, q_threshold, solid_threshold, normalize_q) -# def __dealloc__(self): -# del self.thisptr -# def compute(self, system, neighbors=None): -# r"""Compute the order parameter. + def compute(self, system, neighbors=None): + r"""Compute the order parameter. -# Args: -# system: -# Any object that is a valid argument to -# :class:`freud.locality.NeighborQuery.from_system`. -# neighbors (:class:`freud.locality.NeighborList` or dict, optional): -# Either a :class:`NeighborList ` of -# neighbor pairs to use in the calculation, or a dictionary of -# `query arguments -# `_ -# (Default value: None). -# """ -# cdef: -# freud.locality.NeighborQuery nq -# freud.locality.NeighborList nlist -# freud.locality._QueryArgs qargs -# const float[:, ::1] l_query_points -# unsigned int num_query_points + Args: + system: + Any object that is a valid argument to + :class:`freud.locality.NeighborQuery.from_system`. + neighbors (:class:`freud.locality.NeighborList` or dict, optional): + Either a :class:`NeighborList ` of + neighbor pairs to use in the calculation, or a dictionary of + `query arguments + `_ + (Default value: None). + """ -# 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)) -# return self + 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 diff --git a/freud/order/export-SolidLiquid.cc b/freud/order/export-SolidLiquid.cc index 74020355e..e2abd4c91 100644 --- a/freud/order/export-SolidLiquid.cc +++ b/freud/order/export-SolidLiquid.cc @@ -5,7 +5,6 @@ #include #include #include // NOLINT(misc-include-cleaner): used implicitly -// #include // NOLINT(misc-include-cleaner): used implicitly #include // #include "ManagedArray.h" @@ -23,7 +22,7 @@ void export_SolidLiquid(nanobind::module_& m) { nanobind::class_(m, "SolidLiquid") .def(nanobind::init()) - // .def("getRAArray", &RotationalAutocorrelation::getRAArray) + .def("compute", &SolidLiquid::compute, nanobind::arg("nlist").none(), nanobind::arg("points"), nanobind::arg("qargs")) ; } diff --git a/freud/order/export-Steinhardt.cc b/freud/order/export-Steinhardt.cc index fbf06f545..c933077c2 100644 --- a/freud/order/export-Steinhardt.cc +++ b/freud/order/export-Steinhardt.cc @@ -18,19 +18,6 @@ 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); -// } - -}; // namespace wrap - namespace detail { void export_Steinhardt(nanobind::module_& m) From ac7065dbcba89d37c4e060acc4440ee52a1308a2 Mon Sep 17 00:00:00 2001 From: janbridley Date: Sat, 26 Oct 2024 14:41:07 -0400 Subject: [PATCH 52/91] Finish exporting SolidLiquid --- freud/order.py | 189 +++++++++++++++--------------- freud/order/export-SolidLiquid.cc | 11 ++ 2 files changed, 106 insertions(+), 94 deletions(-) diff --git a/freud/order.py b/freud/order.py index e0ecbf46b..53e310188 100644 --- a/freud/order.py +++ b/freud/order.py @@ -10,6 +10,8 @@ Fourier Transforms. """ +from math import floor + from freud.util import _Compute # , quat, vec3 from freud.errors import FreudDeprecationWarning @@ -709,6 +711,13 @@ class SolidLiquid(_PairCompute): """ 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) @@ -732,115 +741,107 @@ def compute(self, system, neighbors=None): self._cpp_obj.compute(nlist._cpp_obj, nq._cpp_obj, qargs._cpp_obj) return self -# @property -# def l(self): # noqa: E743 -# """unsigned int: Spherical harmonic quantum number l.""" -# return self.thisptr.getL() + @property + def l(self): # noqa: E743 + """unsigned int: Spherical harmonic quantum number l.""" + return self._cpp_obj.getL() -# @property -# def q_threshold(self): -# """float: Value of dot product threshold.""" -# return self.thisptr.getQThreshold() + @property + def q_threshold(self): + """float: Value of dot product threshold.""" + return self._cpp_obj.getQThreshold() -# @property -# def solid_threshold(self): -# """float: Value of number-of-bonds threshold.""" -# return self.thisptr.getSolidThreshold() + @property + def solid_threshold(self): + """float: Value of number-of-bonds threshold.""" + return self._cpp_obj.getSolidThreshold() -# @property -# def normalize_q(self): -# """bool: Whether the dot product is normalized.""" -# return self.thisptr.getNormalizeQ() + @property + def normalize_q(self): + """bool: Whether the dot product is normalized.""" + 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) + @_Compute._computed_property + def cluster_idx(self): + """:math:`\\left(N_{particles}\\right)` :class:`numpy.ndarray`: + Solid-like cluster indices for each particle.""" + 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) + @_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 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) + @_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 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()) + @_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() + @_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 + @_Compute._computed_property + def nlist(self): + """:class:`freud.locality.NeighborList`: Neighbor list of solid-like + bonds.""" + # nlist = freud.locality._nlist_from_cnlist(self._cpp_obj.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) + @_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 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) + def __repr__(self): + 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. + def plot(self, ax=None): + """Plot solid-like cluster distribution. -# Args: -# ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If -# :code:`None`, make a new figure and axis -# (Default value = :code:`None`). + Args: + ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If + :code:`None`, make a new figure and axis + (Default value = :code:`None`). -# Returns: -# (: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) + Returns: + (: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) -# def _repr_png_(self): -# try: -# import freud.plot -# return freud.plot._ax_to_bytes(self.plot()) -# except (AttributeError, ImportError): -# return None + def _repr_png_(self): + try: + import freud.plot + return freud.plot._ax_to_bytes(self.plot()) + except (AttributeError, ImportError): + return None class RotationalAutocorrelation(_Compute): diff --git a/freud/order/export-SolidLiquid.cc b/freud/order/export-SolidLiquid.cc index e2abd4c91..e1c2abacf 100644 --- a/freud/order/export-SolidLiquid.cc +++ b/freud/order/export-SolidLiquid.cc @@ -23,6 +23,17 @@ 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) ; } From da32e83681cc39c9d2b7f43e10c91256449a6cac Mon Sep 17 00:00:00 2001 From: janbridley Date: Sat, 26 Oct 2024 14:46:23 -0400 Subject: [PATCH 53/91] Clean up exports thus far --- freud/order/export-Nematic.cc | 8 -------- freud/order/export-RotationalAutocorrelation.cc | 2 -- freud/order/export-SolidLiquid.cc | 9 +-------- freud/order/export-Steinhardt.cc | 2 -- 4 files changed, 1 insertion(+), 20 deletions(-) diff --git a/freud/order/export-Nematic.cc b/freud/order/export-Nematic.cc index 24c9d9770..4ea8d6bf4 100644 --- a/freud/order/export-Nematic.cc +++ b/freud/order/export-Nematic.cc @@ -8,9 +8,6 @@ #include // NOLINT(misc-include-cleaner): used implicitly #include -// #include "BondHistogramCompute.h" -// #include "NeighborList.h" -// #include "NeighborQuery.h" #include "Nematic.h" #include "VectorMath.h" @@ -39,11 +36,6 @@ nanobind::tuple getNematicDirector(const std::shared_ptr& self) namespace detail { -// void export_PMFT(nanobind::module_& m) -// { -// nanobind::class_(m, "PMFT").def("getPCF", &PMFT::getPCF); -// } - void export_Nematic(nanobind::module_& m) { nanobind::class_(m, "Nematic") diff --git a/freud/order/export-RotationalAutocorrelation.cc b/freud/order/export-RotationalAutocorrelation.cc index 090b0e4de..a3ddc35e3 100644 --- a/freud/order/export-RotationalAutocorrelation.cc +++ b/freud/order/export-RotationalAutocorrelation.cc @@ -12,8 +12,6 @@ #include "RotationalAutocorrelation.h" #include "VectorMath.h" -namespace nb = nanobind; - namespace freud { namespace order { template diff --git a/freud/order/export-SolidLiquid.cc b/freud/order/export-SolidLiquid.cc index e1c2abacf..04e6bc525 100644 --- a/freud/order/export-SolidLiquid.cc +++ b/freud/order/export-SolidLiquid.cc @@ -3,20 +3,13 @@ #include #include -#include #include // NOLINT(misc-include-cleaner): used implicitly #include -// #include "ManagedArray.h" #include "SolidLiquid.h" -// #include "VectorMath.h" -namespace freud { namespace order { - -namespace wrap {}; // namespace wrap - -namespace detail { +namespace freud { namespace order { namespace detail { void export_SolidLiquid(nanobind::module_& m) { diff --git a/freud/order/export-Steinhardt.cc b/freud/order/export-Steinhardt.cc index c933077c2..d989f2959 100644 --- a/freud/order/export-Steinhardt.cc +++ b/freud/order/export-Steinhardt.cc @@ -4,14 +4,12 @@ #include #include #include -// #include // NOLINT(misc-include-cleaner): used implicitly #include #include // NOLINT(misc-include-cleaner): used implicitly #include #include "Steinhardt.h" -// #include "VectorMath.h" namespace freud { namespace order { From 41606ba3fbe3cf4b9a1ec375bfbd49dc830cc615 Mon Sep 17 00:00:00 2001 From: janbridley Date: Sat, 26 Oct 2024 14:55:49 -0400 Subject: [PATCH 54/91] export continuouscoordination --- freud/CMakeLists.txt | 6 ++-- freud/order/export-ContinuousCoordination.cc | 32 ++++++++++++++++++++ freud/order/module-order.cc | 2 ++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 freud/order/export-ContinuousCoordination.cc diff --git a/freud/CMakeLists.txt b/freud/CMakeLists.txt index 90f30095b..04390a20d 100644 --- a/freud/CMakeLists.txt +++ b/freud/CMakeLists.txt @@ -90,8 +90,8 @@ add_library( ${VOROPP_SOURCE_DIR}/pre_container.cc ${VOROPP_SOURCE_DIR}/container_prd.cc order - # order/ContinuousCoordination.h - # order/ContinuousCoordination.cc + order/ContinuousCoordination.h + order/ContinuousCoordination.cc # order/Cubatic.cc # order/Cubatic.h # order/HexaticTranslational.cc @@ -176,7 +176,7 @@ target_set_install_rpath(_locality) # 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-SolidLiquid.cc order/export-ContinuousCoordination.cc) target_link_libraries(_order PUBLIC freud) target_set_install_rpath(_order) diff --git a/freud/order/export-ContinuousCoordination.cc b/freud/order/export-ContinuousCoordination.cc new file mode 100644 index 000000000..54f88f0fc --- /dev/null +++ b/freud/order/export-ContinuousCoordination.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 +// #include +// #include // NOLINT(misc-include-cleaner): used implicitly + +#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("isAverage", &Steinhardt::isAverage) + ; +} + +} // namespace detail + +}; }; // namespace freud::order diff --git a/freud/order/module-order.cc b/freud/order/module-order.cc index dc20b4756..b5b179888 100644 --- a/freud/order/module-order.cc +++ b/freud/order/module-order.cc @@ -9,6 +9,7 @@ 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); } // namespace freud::order::detail using namespace freud::order::detail; @@ -19,4 +20,5 @@ NB_MODULE(_order, module) // NOLINT(misc-use-anonymous-namespace): caused by nan export_RotationalAutocorrelation(module); export_Steinhardt(module); export_SolidLiquid(module); + export_ContinuousCoordination(module); } From ed234cacb95bdf8c7ab53a0d39837f462cfdb04e Mon Sep 17 00:00:00 2001 From: janbridley Date: Sat, 26 Oct 2024 15:07:45 -0400 Subject: [PATCH 55/91] Compiling version of ContinuousCoordination --- freud/locality/NeighborComputeFunctional.h | 2 +- freud/order/ContinuousCoordination.cc | 16 ++++++++-------- freud/order/ContinuousCoordination.h | 9 +++++---- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/freud/locality/NeighborComputeFunctional.h b/freud/locality/NeighborComputeFunctional.h index 8a3d78eec..4c2da8e68 100644 --- a/freud/locality/NeighborComputeFunctional.h +++ b/freud/locality/NeighborComputeFunctional.h @@ -235,7 +235,7 @@ 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/ContinuousCoordination.cc b/freud/order/ContinuousCoordination.cc index 370479eff..38dd1c4a6 100644 --- a/freud/order/ContinuousCoordination.cc +++ b/freud/order/ContinuousCoordination.cc @@ -18,11 +18,11 @@ 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 +32,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..669817edf 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; } @@ -56,7 +57,7 @@ class ContinuousCoordination 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::shared_ptr> m_coordination; //!< number of neighbors array computed }; }; }; // namespace freud::order From 215bb8305c9a89a020b841ef31001f4ea9f206cf Mon Sep 17 00:00:00 2001 From: janbridley Date: Sat, 26 Oct 2024 15:19:51 -0400 Subject: [PATCH 56/91] Python export for ContinuousCoordination --- freud/order.py | 230 +++++++++---------- freud/order/export-ContinuousCoordination.cc | 10 +- 2 files changed, 115 insertions(+), 125 deletions(-) diff --git a/freud/order.py b/freud/order.py index 53e310188..f396cdba3 100644 --- a/freud/order.py +++ b/freud/order.py @@ -915,152 +915,138 @@ def __repr__(self): return f"freud.order.{type(self).__name__}(l={self.l})" -# cdef class ContinuousCoordination(_PairCompute): -# r"""Computes the continuous local coordination number. +class ContinuousCoordination(_PairCompute): + r"""Computes the continuous local coordination number. -# The :class:`ContinuousCoordination` class implements extensions of the Voronoi -# discrete coordination number to the real numbers. The formulas for the -# various implementations are: + The :class:`ContinuousCoordination` class implements extensions of the Voronoi + discrete coordination number to the real numbers. The formulas for the + various implementations are: -# Power: + Power: -# .. math:: - -# CN_p = N^{2.0 - m} \sum_{i=1}^{k} -# {\left[\left(\frac{V_i}{V}\right)^{m}\right]}^{-1} + .. math:: -# Log: + CN_p = N^{2.0 - m} \sum_{i=1}^{k} + {\left[\left(\frac{V_i}{V}\right)^{m}\right]}^{-1} -# .. math:: + Log: -# CN_{log} = \frac{-1}{\log{N}} \sum_{i=1}^{k}\log{\left(\frac{V_i}{V}\right)} + .. math:: -# Exponential: + CN_{log} = \frac{-1}{\log{N}} \sum_{i=1}^{k}\log{\left(\frac{V_i}{V}\right)} -# .. math:: + Exponential: -# CN_{exp} = \sum_{i=1}^{k}\exp{\left(\frac{V_i}{V} - \frac{1}{N} \right)} + .. math:: -# where :math:`k` is the number of neighbors a particle has, :math:`V_i` is -# the volume of the pyramid (or area of the triangle in 2D) whose base is the -# Voronoi polytope facet between the central particle and neighbor :math:`i` -# and whose height is half the distance vector, and :math:`V` is the -# volume/area of the Voronoi polytope. + CN_{exp} = \sum_{i=1}^{k}\exp{\left(\frac{V_i}{V} - \frac{1}{N} \right)} -# Note: -# When using multiple powers, space them out to avoid high correlations. A -# minimum spacing of 2.0 is recommended with even larger values leading to -# less correlation. + where :math:`k` is the number of neighbors a particle has, :math:`V_i` is + the volume of the pyramid (or area of the triangle in 2D) whose base is the + Voronoi polytope facet between the central particle and neighbor :math:`i` + and whose height is half the distance vector, and :math:`V` is the + volume/area of the Voronoi polytope. -# Args: -# powers (list[float], optional): The powers to compute the continuous -# coordination number for. The default value indicates only compute -# for power 2.0. -# (Default value: None) -# compute_log (`bool`, optional): Whether to compute the log continuous -# coordination number. -# (Default value: :code:`True`) -# compute_exp (`bool`, optional): Whether to compute the exp continuous -# coordination number. -# (Default value: :code:`True`) -# """ -# cdef freud._order.ContinuousCoordination* thisptr - -# def __cinit__(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) + Note: + When using multiple powers, space them out to avoid high correlations. A + minimum spacing of 2.0 is recommended with even larger values leading to + less correlation. -# def __dealloc__(self): -# del self.thisptr + Args: + powers (list[float], optional): The powers to compute the continuous + coordination number for. The default value indicates only compute + for power 2.0. + (Default value: None) + compute_log (`bool`, optional): Whether to compute the log continuous + coordination number. + (Default value: :code:`True`) + compute_exp (`bool`, optional): Whether to compute the exp continuous + coordination number. + (Default value: :code:`True`) + """ + def __init__(self, powers=None, compute_log=True, compute_exp=True): + if powers is None: + powers = [2.0] + 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. + def compute(self, system=None, voronoi=None): + r"""Calculates the local coordination number for the specified points. -# Example:: + Example:: -# >>> import freud -# >>> box, points = freud.data.make_random_system(10, 100, seed=0) -# >>> # Compute ContinuousCoordination -# >>> coord = freud.order.ContinuousCoordination([2, 4], True) -# >>> coord.compute(system=(box, points)) -# freud.order.ContinuousCoordination(...) + >>> import freud + >>> box, points = freud.data.make_random_system(10, 100, seed=0) + >>> # Compute ContinuousCoordination + >>> coord = freud.order.ContinuousCoordination([2, 4], True) + >>> coord.compute(system=(box, points)) + freud.order.ContinuousCoordination(...) -# Args: -# system (optional): -# Any object that is a valid argument to -# :class:`freud.locality.NeighborQuery.from_system`. -# (Default value: None). -# voronoi (:class:`freud.locality.Voronoi`, optional): -# A precomputed Voronoi compute object. If provided, the object is -# 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.") -# 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.") -# cpp_voronoi = voronoi -# self.thisptr.compute(cpp_voronoi.thisptr) -# return self + Args: + system (optional): + Any object that is a valid argument to + :class:`freud.locality.NeighborQuery.from_system`. + (Default value: None). + voronoi (:class:`freud.locality.Voronoi`, optional): + A precomputed Voronoi compute object. If provided, the object is + assumed to have been computed already, and system is ignored. + (Default value: None). + """ + if system is None and voronoi is None: + raise ValueError("Must specify system or voronoi.") # noqa: EM101 + 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.") # noqa: EM101 + cpp_voronoi = voronoi + self._cpp_obj.compute(cpp_voronoi._cpp_obj) + return self -# @_Compute._computed_property -# def particle_order(self): -# """(:math:`(N_{points}, N_{coord}`) :class:`numpy.ndarray`: \ -# coordination of points per query point. + @_Compute._computed_property + def particle_order(self): + """(:math:`(N_{points}, N_{coord}`) :class:`numpy.ndarray`: \ + coordination of points per query point. -# Coordination numbers are in order of selected powers, log, and exp. -# """ -# return self.coordination + Coordination numbers are in order of selected powers, log, and exp. + """ + return self.coordination -# @_Compute._computed_property -# def coordination(self): -# """(:math:`(N_{points}, N_{coord}`) :class:`numpy.ndarray`): \ -# coordination of points per query point. + @_Compute._computed_property + def coordination(self): + """(:math:`(N_{points}, N_{coord}`) :class:`numpy.ndarray`): \ + coordination of points per query point. -# 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) + Coordination numbers are in order of selected powers, log, and exp. + """ + return self._cpp_obj.getCoordination().toNumpyArray() -# @property -# def powers(self): -# """list[float]: The powers to compute the continuous coordination number. + @property + def powers(self): + """list[float]: The powers to compute the continuous coordination number. -# Changes to this property are not reflected when computing coordination -# numbers. -# """ -# return self.thisptr.getPowers() + Changes to this property are not reflected when computing coordination + numbers. + """ + return self._cpp_obj.getPowers() -# @property -# def compute_log(self): -# """bool: Whether to compute the log continuous coordination number.""" -# return self.thisptr.getComputeLog() + @property + def compute_log(self): + """bool: Whether to compute the log continuous coordination number.""" + return self._cpp_obj.getComputeLog() -# @property -# def compute_exp(self): -# """bool: Whether to compute the exponential coordination number.""" -# return self.thisptr.getComputeExp() + @property + def compute_exp(self): + """bool: Whether to compute the exponential coordination number.""" + return self._cpp_obj.getComputeExp() -# @property -# def number_of_coordinations(self): -# """int: The number of coordination numbers computed.""" -# return self.thisptr.getNumberOfCoordinations() + @property + def number_of_coordinations(self): + """int: The number of coordination numbers computed.""" + 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, -# ) + def __repr__(self): + return ( + 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/export-ContinuousCoordination.cc b/freud/order/export-ContinuousCoordination.cc index 54f88f0fc..edd85e9b9 100644 --- a/freud/order/export-ContinuousCoordination.cc +++ b/freud/order/export-ContinuousCoordination.cc @@ -4,8 +4,8 @@ #include #include #include -// #include -// #include // NOLINT(misc-include-cleaner): used implicitly +#include +#include // NOLINT(misc-include-cleaner): used implicitly #include @@ -23,7 +23,11 @@ void export_ContinuousCoordination(nanobind::module_& m) nanobind::class_(m, "ContinuousCoordination") .def(nanobind::init, bool, bool>()) .def("compute", &ContinuousCoordination::compute, nanobind::arg("voronoi")) - // .def("isAverage", &Steinhardt::isAverage) + .def("getCoordination", &ContinuousCoordination::getCoordination) + .def("getPowers", &ContinuousCoordination::getPowers) + .def("getComputeLog", &ContinuousCoordination::getComputeLog) + .def("getComputeExp", &ContinuousCoordination::getComputeExp) + .def("getNumberOfCoordinations", &ContinuousCoordination::getNumberOfCoordinations) ; } From b55deaf5ddda9985e97bb3f3fae701f12e479d8a Mon Sep 17 00:00:00 2001 From: janbridley Date: Sat, 26 Oct 2024 15:21:19 -0400 Subject: [PATCH 57/91] Lint cpp files --- freud/locality/NeighborComputeFunctional.h | 12 +++++--- freud/order/ContinuousCoordination.cc | 3 +- freud/order/ContinuousCoordination.h | 6 ++-- freud/order/SolidLiquid.cc | 11 ++++--- freud/order/SolidLiquid.h | 20 ++++++------ freud/order/Steinhardt.cc | 32 ++++++++++++-------- freud/order/Steinhardt.h | 22 +++++++++----- freud/order/Wigner3j.cc | 4 +-- freud/order/Wigner3j.h | 5 +-- freud/order/export-ContinuousCoordination.cc | 5 ++- freud/order/export-SolidLiquid.cc | 11 +++---- freud/order/export-Steinhardt.cc | 8 ++--- freud/util/export-ManagedArray.cc | 2 +- 13 files changed, 79 insertions(+), 62 deletions(-) diff --git a/freud/locality/NeighborComputeFunctional.h b/freud/locality/NeighborComputeFunctional.h index 4c2da8e68..eb53ac7b2 100644 --- a/freud/locality/NeighborComputeFunctional.h +++ b/freud/locality/NeighborComputeFunctional.h @@ -77,7 +77,7 @@ class NeighborListPerPointIterator : public NeighborPerPointIterator private: 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_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 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) +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 std::shared_ptr 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/ContinuousCoordination.cc b/freud/order/ContinuousCoordination.cc index 38dd1c4a6..f83832794 100644 --- a/freud/order/ContinuousCoordination.cc +++ b/freud/order/ContinuousCoordination.cc @@ -22,7 +22,8 @@ void ContinuousCoordination::compute(const std::shared_ptrgetNeighborList(); size_t num_points = nlist->getNumQueryPoints(); - m_coordination = std::make_shared>(std::vector {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 diff --git a/freud/order/ContinuousCoordination.h b/freud/order/ContinuousCoordination.h index 669817edf..0ccc42da1 100644 --- a/freud/order/ContinuousCoordination.h +++ b/freud/order/ContinuousCoordination.h @@ -54,9 +54,9 @@ 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 + 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 }; diff --git a/freud/order/SolidLiquid.cc b/freud/order/SolidLiquid.cc index 41de5a922..096041676 100644 --- a/freud/order/SolidLiquid.cc +++ b/freud/order/SolidLiquid.cc @@ -21,9 +21,11 @@ SolidLiquid::SolidLiquid(unsigned int l, float q_threshold, unsigned int solid_t } // void SolidLiquid::compute(const std::shared_ptr& nlist, -// const std::shared_ptr& points, freud::locality::QueryArgs& qargs) +// const std::shared_ptr& points, +// freud::locality::QueryArgs& qargs) void SolidLiquid::compute(const std::shared_ptr& nlist, - const std::shared_ptr& points, freud::locality::QueryArgs& qargs) + 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); @@ -80,8 +82,9 @@ void SolidLiquid::compute(const std::shared_ptr& solid_nlist.filter(solid_filter.cbegin()); // Save the neighbor counts of solid-like bonds for each query point - m_number_of_connections = std::make_shared>(std::vector {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]; diff --git a/freud/order/SolidLiquid.h b/freud/order/SolidLiquid.h index eba8d6fbf..cf19a3dca 100644 --- a/freud/order/SolidLiquid.h +++ b/freud/order/SolidLiquid.h @@ -85,7 +85,8 @@ class SolidLiquid } //! Compute the Solid-Liquid Order Parameter - void compute(const std::shared_ptr& nlist, const std::shared_ptr& points, + void compute(const std::shared_ptr& nlist, + const std::shared_ptr& points, freud::locality::QueryArgs& qargs); //! Returns largest cluster size. @@ -142,19 +143,20 @@ class SolidLiquid } 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. + 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 - 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 + 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 604659e4b..3ce275ffb 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -41,33 +41,36 @@ void Steinhardt::reallocateArrays(unsigned int Np) const auto num_ls = m_ls.size(); - m_qli = std::make_shared>(std::vector{Np, num_ls}); + m_qli = std::make_shared>(std::vector {Np, num_ls}); if (m_average) { - m_qliAve = std::make_shared>(std::vector{Np, num_ls}); + m_qliAve = std::make_shared>(std::vector {Np, num_ls}); } if (m_wl) { - m_wli = std::make_shared>(std::vector{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_qlmi[l_index] = std::make_shared>>(std::vector{Np, num_ms}); + m_qlmi[l_index] + = std::make_shared>>(std::vector {Np, num_ms}); // m_qlm[l_index].prepare(num_ms); - m_qlm[l_index] = std::make_shared>>(std::vector{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}); + m_qlmiAve[l_index] + = std::make_shared>>(std::vector {Np, num_ms}); } } } void Steinhardt::compute(const std::shared_ptr& nlist, - const std::shared_ptr& points, + const std::shared_ptr& points, const freud::locality::QueryArgs& qargs) { // Allocate and zero out arrays as necessary. @@ -102,7 +105,8 @@ void Steinhardt::compute(const std::shared_ptr& n } void Steinhardt::baseCompute(const std::shared_ptr& nlist, - const std::shared_ptr& points, const freud::locality::QueryArgs& qargs) + 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) @@ -205,7 +209,7 @@ void Steinhardt::baseCompute(const std::shared_ptr& nlist, - const std::shared_ptr& points, + const std::shared_ptr& points, const freud::locality::QueryArgs& qargs) { std::shared_ptr iter; @@ -311,9 +315,10 @@ std::vector Steinhardt::normalizeSystem() } // void Steinhardt::aggregatewl(util::ManagedArray& target, -void Steinhardt::aggregatewl(std::shared_ptr>& target, - const std::vector>>>& source, - const std::shared_ptr>& 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) @@ -334,7 +339,8 @@ void Steinhardt::aggregatewl(std::shared_ptr>& target, { const float normalization = std::sqrt(normalizationfactor) / (*normalization_source)[norm_particle_index + l_index]; - (*target)[target_particle_index + l_index] *= normalization * normalization * normalization; + (*target)[target_particle_index + l_index] + *= normalization * normalization * normalization; } } } diff --git a/freud/order/Steinhardt.h b/freud/order/Steinhardt.h index d8d210374..a22087ead 100644 --- a/freud/order/Steinhardt.h +++ b/freud/order/Steinhardt.h @@ -151,7 +151,7 @@ class Steinhardt } //! Compute the order parameter - void compute(const std::shared_ptr& nlist, + void compute(const std::shared_ptr& nlist, const std::shared_ptr& points, const freud::locality::QueryArgs& qargs); @@ -175,11 +175,13 @@ class Steinhardt void reallocateArrays(unsigned int Np); //! Calculates qlms and the ql order parameter before any further modifications - void baseCompute(const std::shared_ptr& nlist, const std::shared_ptr& points, + 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 std::shared_ptr& nlist, const std::shared_ptr& points, + 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 @@ -204,12 +206,16 @@ class Steinhardt 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_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 - 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 + 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>>> diff --git a/freud/order/Wigner3j.cc b/freud/order/Wigner3j.cc index eb860b023..c2815aaf6 100644 --- a/freud/order/Wigner3j.cc +++ b/freud/order/Wigner3j.cc @@ -6,7 +6,6 @@ #include "Wigner3j.h" - /*! \file Wigner3j.cc * \brief Stores and reduces over Wigner 3j coefficients for l from 0 to 20 */ @@ -19,7 +18,8 @@ inline int lmIndex(int l, int m) } float reduceWigner3j(const std::complex* source, unsigned int l_, const std::vector& wigner3j) -// float reduceWigner3j(const util::ManagedArray>* source, unsigned int l_, const std::vector& wigner3j) +// float reduceWigner3j(const util::ManagedArray>* source, unsigned int l_, const +// std::vector& wigner3j) { /* * Wigner 3j coefficients: diff --git a/freud/order/Wigner3j.h b/freud/order/Wigner3j.h index ce247f4c7..dd4521a45 100644 --- a/freud/order/Wigner3j.h +++ b/freud/order/Wigner3j.h @@ -7,8 +7,8 @@ #include #include -#include "VectorMath.h" #include "ManagedArray.h" +#include "VectorMath.h" /*! \file Wigner3j.h * \brief Stores and reduces over Wigner 3j coefficients for l from 0 to 20 @@ -24,7 +24,8 @@ int lmIndex(int l, int m); // third-order rotational invariant quantity. // source array must be indexed by m, like [0, 1, ..., l, -1, -2, ..., -l]. float reduceWigner3j(const std::complex* source, unsigned int l_, const std::vector& wigner3j); -// float reduceWigner3j(const util::ManagedArray>* source, unsigned int l_, const std::vector& wigner3j); +// float reduceWigner3j(const util::ManagedArray>* source, unsigned int l_, const +// std::vector& wigner3j); std::vector getWigner3j(unsigned int l); // All Wigner 3j coefficients created using sympy diff --git a/freud/order/export-ContinuousCoordination.cc b/freud/order/export-ContinuousCoordination.cc index edd85e9b9..9323ac735 100644 --- a/freud/order/export-ContinuousCoordination.cc +++ b/freud/order/export-ContinuousCoordination.cc @@ -4,8 +4,8 @@ #include #include #include -#include #include // NOLINT(misc-include-cleaner): used implicitly +#include #include @@ -27,8 +27,7 @@ void export_ContinuousCoordination(nanobind::module_& m) .def("getPowers", &ContinuousCoordination::getPowers) .def("getComputeLog", &ContinuousCoordination::getComputeLog) .def("getComputeExp", &ContinuousCoordination::getComputeExp) - .def("getNumberOfCoordinations", &ContinuousCoordination::getNumberOfCoordinations) - ; + .def("getNumberOfCoordinations", &ContinuousCoordination::getNumberOfCoordinations); } } // namespace detail diff --git a/freud/order/export-SolidLiquid.cc b/freud/order/export-SolidLiquid.cc index 04e6bc525..de6ea7229 100644 --- a/freud/order/export-SolidLiquid.cc +++ b/freud/order/export-SolidLiquid.cc @@ -8,14 +8,14 @@ #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("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) @@ -26,10 +26,7 @@ void export_SolidLiquid(nanobind::module_& m) .def("getClusterSizes", &SolidLiquid::getClusterSizes) .def("getLargestClusterSize", &SolidLiquid::getLargestClusterSize) .def("getNList", &SolidLiquid::getNList) - .def("getNumberOfConnections", &SolidLiquid::getNumberOfConnections) - ; + .def("getNumberOfConnections", &SolidLiquid::getNumberOfConnections); } -} // namespace detail - -}; }; // namespace freud::order +}}; }; // namespace freud::order::detail diff --git a/freud/order/export-Steinhardt.cc b/freud/order/export-Steinhardt.cc index d989f2959..6b2d5cb46 100644 --- a/freud/order/export-Steinhardt.cc +++ b/freud/order/export-Steinhardt.cc @@ -4,8 +4,8 @@ #include #include #include -#include #include // NOLINT(misc-include-cleaner): used implicitly +#include #include @@ -22,7 +22,8 @@ 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("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) @@ -31,8 +32,7 @@ void export_Steinhardt(nanobind::module_& m) .def("getOrder", &Steinhardt::getOrder) .def("getParticleOrder", &Steinhardt::getParticleOrder) .def("getQlm", &Steinhardt::getQlm) - .def("getQl", &Steinhardt::getQl) - ; + .def("getQl", &Steinhardt::getQl); } } // namespace detail diff --git a/freud/util/export-ManagedArray.cc b/freud/util/export-ManagedArray.cc index a9d420bea..a8b8645a0 100644 --- a/freud/util/export-ManagedArray.cc +++ b/freud/util/export-ManagedArray.cc @@ -1,9 +1,9 @@ // 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 "VectorMath.h" #include "export-ManagedArray.h" From f59ce2385b42c7a2b97f723dbe8b7b73cd6d9e43 Mon Sep 17 00:00:00 2001 From: janbridley Date: Sat, 26 Oct 2024 15:42:41 -0400 Subject: [PATCH 58/91] export cubatic --- freud/CMakeLists.txt | 8 ++++-- freud/order.py | 49 ++++++++++++++------------------- freud/order/export-Cubatic.cc | 52 +++++++++++++++++++++++++++++++++++ freud/order/module-order.cc | 3 +- 4 files changed, 80 insertions(+), 32 deletions(-) create mode 100644 freud/order/export-Cubatic.cc diff --git a/freud/CMakeLists.txt b/freud/CMakeLists.txt index 04390a20d..73a8c1a5e 100644 --- a/freud/CMakeLists.txt +++ b/freud/CMakeLists.txt @@ -92,8 +92,8 @@ add_library( order order/ContinuousCoordination.h order/ContinuousCoordination.cc - # order/Cubatic.cc - # order/Cubatic.h + order/Cubatic.cc + order/Cubatic.h # order/HexaticTranslational.cc # order/HexaticTranslational.h order/Nematic.cc @@ -176,7 +176,9 @@ target_set_install_rpath(_locality) # 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-SolidLiquid.cc order/export-ContinuousCoordination.cc + order/export-Cubatic.cc +) target_link_libraries(_order PUBLIC freud) target_set_install_rpath(_order) diff --git a/freud/order.py b/freud/order.py index f396cdba3..b0e8222ce 100644 --- a/freud/order.py +++ b/freud/order.py @@ -41,37 +41,30 @@ # _always_ do that, or you will have segfaults # np.import_array() -print("ADFHAFGJAKFHAJ") +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. -# cdef 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. - -# Args: -# t_initial (float): -# Starting temperature. -# t_final (float): -# Final temperature. -# scale (float): -# Scaling factor to reduce temperature. -# n_replicates (unsigned int, optional): -# Number of replicate simulated annealing runs. -# (Default value = :code:`1`). -# 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): -# if seed is None: -# seed = int(time.time()) + Args: + t_initial (float): + Starting temperature. + t_final (float): + Final temperature. + scale (float): + Scaling factor to reduce temperature. + n_replicates (unsigned int, optional): + Number of replicate simulated annealing runs. + (Default value = :code:`1`). + seed (unsigned int, optional): + Random seed to use in calculations. If :code:`None`, system time is used. + (Default value = :code:`None`). + """ -# self.thisptr = new freud._order.Cubatic( -# t_initial, t_final, scale, n_replicates, seed) + def __init__(self, t_initial, t_final, scale, n_replicates=1, seed=None): + if seed is None: + seed = int(time.time()) -# 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. diff --git a/freud/order/export-Cubatic.cc b/freud/order/export-Cubatic.cc new file mode 100644 index 000000000..f93afd717 --- /dev/null +++ b/freud/order/export-Cubatic.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 "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 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, "Cubatic") + .def(nanobind::init()) + .def("compute", &wrap::computeCubatic, 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/module-order.cc b/freud/order/module-order.cc index b5b179888..484ad37c8 100644 --- a/freud/order/module-order.cc +++ b/freud/order/module-order.cc @@ -10,6 +10,7 @@ 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); } // namespace freud::order::detail using namespace freud::order::detail; @@ -20,5 +21,5 @@ NB_MODULE(_order, module) // NOLINT(misc-use-anonymous-namespace): caused by nan export_RotationalAutocorrelation(module); export_Steinhardt(module); export_SolidLiquid(module); - export_ContinuousCoordination(module); + export_Cubatic(module); } From 2cc36c231706f3c606ca583ad9c570b5372efe6d Mon Sep 17 00:00:00 2001 From: janbridley Date: Sat, 26 Oct 2024 15:47:47 -0400 Subject: [PATCH 59/91] Replace prepare --- freud/order/Cubatic.cc | 9 ++++++--- freud/order/Cubatic.h | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/freud/order/Cubatic.cc b/freud/order/Cubatic.cc index aeadf4b34..a44c253bd 100644 --- a/freud/order/Cubatic.cc +++ b/freud/order/Cubatic.cc @@ -236,11 +236,13 @@ 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.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}); + // m_global_tensor.prepare({3, 3, 3, 3}); + 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 @@ -329,7 +331,8 @@ void Cubatic::compute(quat* orientations, unsigned int num_orientations) } } - m_cubatic_tensor.prepare({3, 3, 3, 3}); + // m_cubatic_tensor.prepare({3, 3, 3, 3}); + 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]; diff --git a/freud/order/Cubatic.h b/freud/order/Cubatic.h index fabcfef8e..a45c10a76 100644 --- a/freud/order/Cubatic.h +++ b/freud/order/Cubatic.h @@ -180,10 +180,10 @@ 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. From 3d049a34c9b5538196ffb337e196765ded01af79 Mon Sep 17 00:00:00 2001 From: janbridley Date: Sat, 26 Oct 2024 16:44:36 -0400 Subject: [PATCH 60/91] Finish cubatic --- freud/order.py | 139 +++++++++++++++------------------- freud/order/Cubatic.cc | 15 ++-- freud/order/Cubatic.h | 6 +- freud/order/export-Cubatic.cc | 30 +++++--- 4 files changed, 91 insertions(+), 99 deletions(-) diff --git a/freud/order.py b/freud/order.py index b0e8222ce..df2e7bdcd 100644 --- a/freud/order.py +++ b/freud/order.py @@ -66,94 +66,79 @@ def __init__(self, t_initial, t_final, scale, n_replicates=1, seed=None): 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. - -# Args: -# 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] + def compute(self, orientations): + r"""Calculates the per-particle and global order parameter. -# self.thisptr.compute( -# &l_orientations[0, 0], num_particles) -# return self + Args: + orientations ((:math:`N_{particles}`, 4) :class:`numpy.ndarray`): + Orientations as angles to use in computation. + """ + 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() + @property + def t_initial(self): + """float: The value of the initial temperature.""" + return self._cpp_obj.getTInitial() -# @property -# def t_final(self): -# """float: The value of the final temperature.""" -# return self.thisptr.getTFinal() + @property + def t_final(self): + """float: The value of the final temperature.""" + return self._cpp_obj.getTFinal() -# @property -# def scale(self): -# """float: The scale.""" -# return self.thisptr.getScale() + @property + def scale(self): + """float: The scale.""" + return self._cpp_obj.getScale() -# @property -# def n_replicates(self): -# """unsigned int: Number of replicate simulated annealing runs.""" -# return self.thisptr.getNReplicates() + @property + def n_replicates(self): + """unsigned int: Number of replicate simulated annealing runs.""" + return self._cpp_obj.getNReplicates() -# @property -# def seed(self): -# """unsigned int: Random seed to use in calculations.""" -# return self.thisptr.getSeed() + @property + def seed(self): + """unsigned int: Random seed to use in calculations.""" + return self._cpp_obj.getSeed() -# @_Compute._computed_property -# def order(self): -# """float: Cubatic order parameter of the system.""" -# return self.thisptr.getCubaticOrderParameter() + @_Compute._computed_property + def order(self): + """float: Cubatic order parameter of the system.""" + 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) + @_Compute._computed_property + def orientation(self): + """:math:`\\left(4 \\right)` :class:`numpy.ndarray`: The quaternion of + global orientation.""" + 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) + @_Compute._computed_property + def particle_order(self): + """:math:`\\left(N_{particles} \\right)` :class:`numpy.ndarray`: Order + parameter.""" + 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) + @_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 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) + @_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 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) + def __repr__(self): + 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})" + ) class Nematic(_Compute): @@ -565,7 +550,7 @@ def particle_order(self): Variant of the Steinhardt order parameter for each particle (filled with :code:`nan` for particles with no neighbors).""" p_orders = self._cpp_obj.getParticleOrder().toNumpyArray() - return np.ravel(p_orders) if p_orders.shape[1] == 1 else p_orders + return np.ravel(p_orders) if p_orders.shape[1] == 1 else p_orders @_Compute._computed_property def ql(self): diff --git a/freud/order/Cubatic.cc b/freud/order/Cubatic.cc index a44c253bd..f2aa5f9fc 100644 --- a/freud/order/Cubatic.cc +++ b/freud/order/Cubatic.cc @@ -71,7 +71,11 @@ tensor4 tensor4::operator*(const float& b) const void tensor4::copyToManagedArray(util::ManagedArray& ma) { - std::copy(data.begin(), data.end(), ma.get()); + // TODO: this may be possible with std::copy but this works as well. + for (unsigned int i = 0; i < 81; i++) + { + ma[i] = data[i]; + } } //! Complete tensor contraction. @@ -236,14 +240,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}); m_global_tensor = std::make_shared>(std::vector {3, 3, 3, 3}); - global_tensor.copyToManagedArray(m_global_tensor); + 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 @@ -331,9 +333,8 @@ void Cubatic::compute(quat* orientations, unsigned int num_orientations) } } - // m_cubatic_tensor.prepare({3, 3, 3, 3}); m_cubatic_tensor = std::make_shared>(std::vector {3, 3, 3, 3}); - p_cubatic_tensor[max_idx].copyToManagedArray(m_cubatic_tensor); + 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]; @@ -344,7 +345,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 a45c10a76..9114bcc35 100644 --- a/freud/order/Cubatic.h +++ b/freud/order/Cubatic.h @@ -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; } diff --git a/freud/order/export-Cubatic.cc b/freud/order/export-Cubatic.cc index f93afd717..be0466089 100644 --- a/freud/order/export-Cubatic.cc +++ b/freud/order/export-Cubatic.cc @@ -4,8 +4,7 @@ #include #include #include -// #include // NOLINT(misc-include-cleaner): used implicitly -// #include // NOLINT(misc-include-cleaner): used implicitly +#include // NOLINT(misc-include-cleaner): used implicitly #include #include "Cubatic.h" @@ -27,24 +26,31 @@ void computeCubatic(const std::shared_ptr& self, 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); -// } +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_Nematic(nanobind::module_& m) +void export_Cubatic(nanobind::module_& m) { nanobind::class_(m, "Cubatic") .def(nanobind::init()) .def("compute", &wrap::computeCubatic, nanobind::arg("orientations")) - // .def("getNematicOrderParameter", &Nematic::getNematicOrderParameter) - // .def("getNematicDirector", &wrap::getNematicDirector) - // .def("getParticleTensor", &Nematic::getParticleTensor) - // .def("getNematicTensor", &Nematic::getNematicTensor); + .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 From db0696253e3c3b519e185aeab0e740c04f3b9138 Mon Sep 17 00:00:00 2001 From: janbridley Date: Sat, 26 Oct 2024 16:45:19 -0400 Subject: [PATCH 61/91] clang format --- freud/order/Cubatic.h | 6 ++++-- freud/order/export-Cubatic.cc | 3 +-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/freud/order/Cubatic.h b/freud/order/Cubatic.h index 9114bcc35..5e5d35b5f 100644 --- a/freud/order/Cubatic.h +++ b/freud/order/Cubatic.h @@ -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. - std::shared_ptr> m_particle_order_parameter; //!< The per-particle value of the order parameter. + 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. - std::shared_ptr> 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/export-Cubatic.cc b/freud/order/export-Cubatic.cc index be0466089..8a5dbd0e1 100644 --- a/freud/order/export-Cubatic.cc +++ b/freud/order/export-Cubatic.cc @@ -49,8 +49,7 @@ void export_Cubatic(nanobind::module_& m) .def("getCubaticOrientation", &wrap::getCubaticOrientation) .def("getParticleOrderParameter", &Cubatic::getParticleOrderParameter) .def("getGlobalTensor", &Cubatic::getGlobalTensor) - .def("getCubaticTensor", &Cubatic::getCubaticTensor) - ; + .def("getCubaticTensor", &Cubatic::getCubaticTensor); } } // namespace detail From d5e0abff880b899ae42b7263972bd5efb21d2aba Mon Sep 17 00:00:00 2001 From: janbridley Date: Sat, 26 Oct 2024 16:47:40 -0400 Subject: [PATCH 62/91] Export ContinuousCoordination properly --- freud/order/module-order.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/freud/order/module-order.cc b/freud/order/module-order.cc index 484ad37c8..316d860f6 100644 --- a/freud/order/module-order.cc +++ b/freud/order/module-order.cc @@ -21,5 +21,6 @@ NB_MODULE(_order, module) // NOLINT(misc-use-anonymous-namespace): caused by nan export_RotationalAutocorrelation(module); export_Steinhardt(module); export_SolidLiquid(module); + export_ContinuousCoordination(module); export_Cubatic(module); } From 577376d3f3f8cf9bc31ba9d252682376f10a3897 Mon Sep 17 00:00:00 2001 From: Gabby Jones Date: Mon, 4 Nov 2024 13:17:00 -0500 Subject: [PATCH 63/91] gabby trying to merge, unhelpful message --- freud/order/HexaticTranslational.cc | 31 ++++++---- freud/order/HexaticTranslational.h | 22 ++++++-- freud/order/export-HexaticTranslational.cc | 66 ++++++++++++++++++++++ 3 files changed, 104 insertions(+), 15 deletions(-) create mode 100644 freud/order/export-HexaticTranslational.cc diff --git a/freud/order/HexaticTranslational.cc b/freud/order/HexaticTranslational.cc index ba6c932c0..989048a9b 100644 --- a/freud/order/HexaticTranslational.cc +++ b/freud/order/HexaticTranslational.cc @@ -2,25 +2,32 @@ // This file is from the freud project, released under the BSD 3-Clause License. #include "HexaticTranslational.h" +#include "ManagedArray.h" +#include "NeighborList.h" +#include "NeighborQuery.h" +#include "NeighborPerPointIterator.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 vec3* 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.prepare(Np); + m_psi_array = std::make_shared>>(std::vector{Np}); freud::locality::loopOverNeighborsIterator( - points, points->getPoints(), Np, qargs, nlist, + &*points, points->getPoints(), Np, qargs, &*nlist, [&](size_t i, const std::shared_ptr& ppiter) { float total_weight(0); const vec3 ref((*points)[i]); @@ -32,24 +39,28 @@ 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(const freud::locality::NeighborList* nlist, +void Hexatic::compute(std::shared_ptr nlist, + const std::shared_ptr& points, + //const vec3* points, + const freud::locality::QueryArgs& qargs) + //const freud::locality::NeighborQuery* points, { computeGeneral( [this](const vec3& delta) { diff --git a/freud/order/HexaticTranslational.h b/freud/order/HexaticTranslational.h index 3651ee6af..3be5ac076 100644 --- a/freud/order/HexaticTranslational.h +++ b/freud/order/HexaticTranslational.h @@ -50,14 +50,21 @@ 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, + 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); + //void computeGeneral(Func func, const freud::locality::NeighborList* nlist, + // const freud::locality::NeighborQuery* points, + // 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 +80,13 @@ 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 vec3* points, + const freud::locality::QueryArgs& qargs); + //void compute(const freud::locality::NeighborList* nlist, const freud::locality::NeighborQuery* points, + // freud::locality::QueryArgs qargs); }; }; }; // end namespace freud::order diff --git a/freud/order/export-HexaticTranslational.cc b/freud/order/export-HexaticTranslational.cc new file mode 100644 index 000000000..a5bfc6a30 --- /dev/null +++ b/freud/order/export-HexaticTranslational.cc @@ -0,0 +1,66 @@ +#include +#include +#include +#include // NOLINT(misc-include-cleaner): used implicitly +#include // NOLINT(misc-include-cleaner): used implicitly +#include + +// #include "BondHistogramCompute.h" +#include "NeighborList.h" +#include "NeighborQuery.h" +#include "HexaticTranslational.h" +#include "VectorMath.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, + //nanobind::shape<-1,3>>& points, + const locality::QueryArgs& qargs + ) +{ + //auto* points_data = reinterpret_cast*>(points.data()); + + self->compute(std::move(nlist), points, qargs); +} + +//nanobind::tuple getHexaticTranslationalDirector(const std::shared_ptr& self) +//{ +// vec3 nem_d_cpp = self->getHexaticTranslationalDirector(); +// return nanobind::make_tuple(nem_d_cpp.x, nem_d_cpp.y, nem_d_cpp.z); +//} +}; // namespace wrap + + +namespace detail { + +// void export_PMFT(nanobind::module_& m) +// { +// nanobind::class_(m, "PMFT").def("getPCF", &PMFT::getPCF); +// } + +void export_HexaticTranslational(nanobind::module_& m) +{ + //nanobind::class_(m, "HexaticTranslational") + //nanobind::class_(m, "Hexatic") + //nanobind::class_(m, "HexaticTranslational") + nanobind::class_(m, "Hexatic") + .def(nanobind::init()) + //.def(nanobind::init<>()) + .def("compute", &wrap::computeHexaticTranslational, nanobind::arg("nlist").none(), nanobind::arg("points"), nanobind::arg("qargs")) + //.def("getOrder", &Hexatic::HexaticTranslational::getOrder) + //.def("getHexaticTranslationalDirector",&wrap::getHexaticTranslationalDirector) + //.def("getParticleTensor",&Hexatic::HexaticTranslational::getParticleTensor) + //.def("getHexaticTranslationalTensor",&HexaticTranslational::getHexaticTranslationalTensor) + ; +} + +} // namespace detail + +}; }; // end namespace freud::pmft From 3989a5d817a858cc24f4f159f41eedb3ce940356 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 4 Nov 2024 13:48:06 -0500 Subject: [PATCH 64/91] Export HexaticTranslational --- freud/CMakeLists.txt | 6 +- freud/order.py | 293 ++++++++++----------- freud/order/HexaticTranslational.cc | 2 +- freud/order/HexaticTranslational.h | 2 +- freud/order/export-HexaticTranslational.cc | 27 +- freud/order/module-order.cc | 2 + 6 files changed, 150 insertions(+), 182 deletions(-) diff --git a/freud/CMakeLists.txt b/freud/CMakeLists.txt index 73a8c1a5e..aefe4d217 100644 --- a/freud/CMakeLists.txt +++ b/freud/CMakeLists.txt @@ -94,8 +94,8 @@ add_library( order/ContinuousCoordination.cc order/Cubatic.cc order/Cubatic.h - # order/HexaticTranslational.cc - # order/HexaticTranslational.h + order/HexaticTranslational.cc + order/HexaticTranslational.h order/Nematic.cc order/Nematic.h order/RotationalAutocorrelation.cc @@ -177,7 +177,7 @@ target_set_install_rpath(_locality) 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-Cubatic.cc order/export-HexaticTranslational.cc ) target_link_libraries(_order PUBLIC freud) target_set_install_rpath(_order) diff --git a/freud/order.py b/freud/order.py index df2e7bdcd..0aa5e55dd 100644 --- a/freud/order.py +++ b/freud/order.py @@ -230,159 +230,146 @@ def __repr__(self): return f"freud.order.{type(self).__name__}()" -# cdef 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 -# :math:`k = 6`) is analogous to Steinhardt order parameters, and is used to -# measure order in the bonds of 2D systems. - -# The :math:`k`-atic order parameter for a particle :math:`i` and its -# :math:`N_b` neighbors :math:`j` is given by: - -# :math:`\psi_k \left( i \right) = \frac{1}{N_b} -# \sum \limits_{j=1}^{N_b} e^{i k \phi_{ij}}` - -# The parameter :math:`k` governs the symmetry of the order parameter and -# typically matches the number of neighbors to be found for each particle. -# The quantity :math:`\phi_{ij}` is the angle between the -# vector :math:`r_{ij}` and :math:`\left(1, 0\right)`. - -# If the weighted mode is enabled, contributions of each neighbor are -# weighted. Neighbor weights :math:`w_{ij}` default to 1 but are defined for a -# :class:`freud.locality.NeighborList` from :class:`freud.locality.Voronoi` -# or one with user-provided weights. The formula is modified as follows: - -# :math:`\psi'_k \left( i \right) = \frac{1}{\sum_{j=1}^{N_b} w_{ij}} -# \sum \limits_{j=1}^{N_b} w_{ij} e^{i k \phi_{ij}}` - -# The hexatic order parameter as written above is **complex-valued**. The -# **magnitude** of the complex value, -# :code:`np.abs(hex_order.particle_order)`, is frequently what is desired -# when determining the :math:`k`-atic order for each particle. The complex -# phase angle :code:`np.angle(hex_order.particle_order)` indicates the -# orientation of the bonds as an angle measured counterclockwise from the -# vector :math:`\left(1, 0\right)`. The complex valued order parameter is -# not rotationally invariant because of this phase angle, but the magnitude -# *is* rotationally invariant. - -# .. note:: -# **2D:** :class:`freud.order.Hexatic` is only defined for 2D systems. -# The points must be passed in as :code:`[x, y, 0]`. - -# Args: -# k (unsigned int, optional): -# Symmetry of order parameter (Default value = :code:`6`). -# weighted (bool, optional): -# Determines whether to use neighbor weights in the computation of -# spherical harmonics over neighbors. If enabled and used with a -# 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 compute(self, system, neighbors=None): -# r"""Calculates the hexatic order parameter. - -# Example:: - -# >>> box, points = freud.data.make_random_system( -# ... box_size=10, num_points=100, is2D=True, seed=0) -# >>> # Compute the hexatic (6-fold) order for the 2D system -# >>> hex_order = freud.order.Hexatic(k=6) -# >>> hex_order.compute(system=(box, points)) -# freud.order.Hexatic(...) -# >>> print(hex_order.particle_order) -# [...] - -# Args: -# system: -# Any object that is a valid argument to -# :class:`freud.locality.NeighborQuery.from_system`. -# neighbors (:class:`freud.locality.NeighborList` or dict, optional): -# Either a :class:`NeighborList ` of -# neighbor pairs to use in the calculation, or a dictionary of -# `query arguments -# `_ -# (Default value: None). -# """ # noqa: E501 -# cdef: -# freud.locality.NeighborQuery nq -# freud.locality.NeighborList nlist -# freud.locality._QueryArgs qargs -# const float[:, ::1] l_query_points -# unsigned int num_query_points - -# nq, nlist, qargs, l_query_points, num_query_points = \ -# self._preprocess_arguments(system, neighbors=neighbors) -# self.thisptr.compute(nlist.get_ptr(), -# nq.get_ptr(), dereference(qargs.thisptr)) -# return self - -# @property -# def default_query_args(self): -# """The default query arguments are -# :code:`{'mode': 'nearest', 'num_neighbors': self.k}`.""" -# return dict(mode="nearest", num_neighbors=self.k) - -# @_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.getOrder(), -# freud.util.arr_type_t.COMPLEX_FLOAT) - -# @property -# def k(self): -# """unsigned int: Symmetry of the order parameter.""" -# return self.thisptr.getK() - -# @property -# def weighted(self): -# """bool: Whether neighbor weights were used in the computation.""" -# return self.thisptr.isWeighted() - -# def __repr__(self): -# return "freud.order.{cls}(k={k}, weighted={weighted})".format( -# cls=type(self).__name__, k=self.k, weighted=self.weighted) - -# def plot(self, ax=None): -# """Plot order parameter distribution. - -# Args: -# ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If -# :code:`None`, make a new figure and axis -# (Default value = :code:`None`). - -# Returns: -# (: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) - -# return freud.plot.histogram_plot( -# np.absolute(self.particle_order), -# title="Hexatic Order Parameter " + xlabel, -# xlabel=xlabel, -# ylabel=r"Number of particles", -# ax=ax) - -# def _repr_png_(self): -# try: -# import freud.plot -# return freud.plot._ax_to_bytes(self.plot()) -# except (AttributeError, ImportError): -# return None +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 + :math:`k = 6`) is analogous to Steinhardt order parameters, and is used to + measure order in the bonds of 2D systems. + + The :math:`k`-atic order parameter for a particle :math:`i` and its + :math:`N_b` neighbors :math:`j` is given by: + + :math:`\psi_k \left( i \right) = \frac{1}{N_b} + \sum \limits_{j=1}^{N_b} e^{i k \phi_{ij}}` + + The parameter :math:`k` governs the symmetry of the order parameter and + typically matches the number of neighbors to be found for each particle. + The quantity :math:`\phi_{ij}` is the angle between the + vector :math:`r_{ij}` and :math:`\left(1, 0\right)`. + + If the weighted mode is enabled, contributions of each neighbor are + weighted. Neighbor weights :math:`w_{ij}` default to 1 but are defined for a + :class:`freud.locality.NeighborList` from :class:`freud.locality.Voronoi` + or one with user-provided weights. The formula is modified as follows: + + :math:`\psi'_k \left( i \right) = \frac{1}{\sum_{j=1}^{N_b} w_{ij}} + \sum \limits_{j=1}^{N_b} w_{ij} e^{i k \phi_{ij}}` + + The hexatic order parameter as written above is **complex-valued**. The + **magnitude** of the complex value, + :code:`np.abs(hex_order.particle_order)`, is frequently what is desired + when determining the :math:`k`-atic order for each particle. The complex + phase angle :code:`np.angle(hex_order.particle_order)` indicates the + orientation of the bonds as an angle measured counterclockwise from the + vector :math:`\left(1, 0\right)`. The complex valued order parameter is + not rotationally invariant because of this phase angle, but the magnitude + *is* rotationally invariant. + + .. note:: + **2D:** :class:`freud.order.Hexatic` is only defined for 2D systems. + The points must be passed in as :code:`[x, y, 0]`. + + Args: + k (unsigned int, optional): + Symmetry of order parameter (Default value = :code:`6`). + weighted (bool, optional): + Determines whether to use neighbor weights in the computation of + spherical harmonics over neighbors. If enabled and used with a + Voronoi neighbor list, this results in the 2D Minkowski Structure + Metrics :math:`\psi'_k` :cite:`Mickel2013` (Default value = + :code:`False`). + """ + + 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. + + Example:: + + >>> box, points = freud.data.make_random_system( + ... box_size=10, num_points=100, is2D=True, seed=0) + >>> # Compute the hexatic (6-fold) order for the 2D system + >>> hex_order = freud.order.Hexatic(k=6) + >>> hex_order.compute(system=(box, points)) + freud.order.Hexatic(...) + >>> print(hex_order.particle_order) + [...] + + Args: + system: + Any object that is a valid argument to + :class:`freud.locality.NeighborQuery.from_system`. + neighbors (:class:`freud.locality.NeighborList` or dict, optional): + Either a :class:`NeighborList ` of + neighbor pairs to use in the calculation, or a dictionary of + `query arguments + `_ + (Default value: None). + """ # noqa: E501 + + 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 default_query_args(self): + """The default query arguments are + :code:`{'mode': 'nearest', 'num_neighbors': self.k}`.""" + return dict(mode="nearest", num_neighbors=self.k) + + @_Compute._computed_property + def particle_order(self): + """:math:`\\left(N_{particles} \\right)` :class:`numpy.ndarray`: Order + parameter.""" + return self._cpp_obj.getOrder().toNumpyArray() + + @property + def k(self): + """unsigned int: Symmetry of the order parameter.""" + return self._cpp_obj.getK() + + @property + def weighted(self): + """bool: Whether neighbor weights were used in the computation.""" + 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) + + def plot(self, ax=None): + """Plot order parameter distribution. + + Args: + ax (:class:`matplotlib.axes.Axes`, optional): Axis to plot on. If + :code:`None`, make a new figure and axis + (Default value = :code:`None`). + + Returns: + (: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) + + return freud.plot.histogram_plot( + np.absolute(self.particle_order), + title="Hexatic Order Parameter " + xlabel, + xlabel=xlabel, + ylabel=r"Number of particles", + ax=ax) + + def _repr_png_(self): + try: + import freud.plot + return freud.plot._ax_to_bytes(self.plot()) + except (AttributeError, ImportError): + return None class Steinhardt(_PairCompute): diff --git a/freud/order/HexaticTranslational.cc b/freud/order/HexaticTranslational.cc index 989048a9b..8a1217ae8 100644 --- a/freud/order/HexaticTranslational.cc +++ b/freud/order/HexaticTranslational.cc @@ -27,7 +27,7 @@ void HexaticTranslational::computeGeneral(Func func, const std::shared_ptr>>(std::vector{Np}); freud::locality::loopOverNeighborsIterator( - &*points, points->getPoints(), Np, qargs, &*nlist, + points, points->getPoints(), Np, qargs, nlist, [&](size_t i, const std::shared_ptr& ppiter) { float total_weight(0); const vec3 ref((*points)[i]); diff --git a/freud/order/HexaticTranslational.h b/freud/order/HexaticTranslational.h index 3be5ac076..379ca9c95 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; } diff --git a/freud/order/export-HexaticTranslational.cc b/freud/order/export-HexaticTranslational.cc index a5bfc6a30..9af165624 100644 --- a/freud/order/export-HexaticTranslational.cc +++ b/freud/order/export-HexaticTranslational.cc @@ -2,14 +2,11 @@ #include #include #include // NOLINT(misc-include-cleaner): used implicitly -#include // NOLINT(misc-include-cleaner): used implicitly #include -// #include "BondHistogramCompute.h" #include "NeighborList.h" #include "NeighborQuery.h" #include "HexaticTranslational.h" -#include "VectorMath.h" namespace freud { namespace order { @@ -21,43 +18,25 @@ namespace wrap { void computeHexaticTranslational(const std::shared_ptr& self, std::shared_ptr nlist, std::shared_ptr& points, - //nanobind::shape<-1,3>>& points, const locality::QueryArgs& qargs ) { - //auto* points_data = reinterpret_cast*>(points.data()); - self->compute(std::move(nlist), points, qargs); } -//nanobind::tuple getHexaticTranslationalDirector(const std::shared_ptr& self) -//{ -// vec3 nem_d_cpp = self->getHexaticTranslationalDirector(); -// return nanobind::make_tuple(nem_d_cpp.x, nem_d_cpp.y, nem_d_cpp.z); -//} }; // namespace wrap namespace detail { -// void export_PMFT(nanobind::module_& m) -// { -// nanobind::class_(m, "PMFT").def("getPCF", &PMFT::getPCF); -// } - void export_HexaticTranslational(nanobind::module_& m) { - //nanobind::class_(m, "HexaticTranslational") - //nanobind::class_(m, "Hexatic") - //nanobind::class_(m, "HexaticTranslational") nanobind::class_(m, "Hexatic") .def(nanobind::init()) - //.def(nanobind::init<>()) .def("compute", &wrap::computeHexaticTranslational, nanobind::arg("nlist").none(), nanobind::arg("points"), nanobind::arg("qargs")) - //.def("getOrder", &Hexatic::HexaticTranslational::getOrder) - //.def("getHexaticTranslationalDirector",&wrap::getHexaticTranslationalDirector) - //.def("getParticleTensor",&Hexatic::HexaticTranslational::getParticleTensor) - //.def("getHexaticTranslationalTensor",&HexaticTranslational::getHexaticTranslationalTensor) + .def("getK", &HexaticTranslational::getK) + .def("getOrder", &HexaticTranslational::getOrder) + .def("isWeighted", &HexaticTranslational::isWeighted) ; } diff --git a/freud/order/module-order.cc b/freud/order/module-order.cc index 316d860f6..eaf3da6c3 100644 --- a/freud/order/module-order.cc +++ b/freud/order/module-order.cc @@ -11,6 +11,7 @@ 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; @@ -23,4 +24,5 @@ NB_MODULE(_order, module) // NOLINT(misc-use-anonymous-namespace): caused by nan export_SolidLiquid(module); export_ContinuousCoordination(module); export_Cubatic(module); + export_HexaticTranslational(module); } From 08c8eb18bfdd6621e947deb89fc884cc7e74342d Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 4 Nov 2024 13:48:35 -0500 Subject: [PATCH 65/91] pre-commit autofix --- freud/CMakeLists.txt | 15 ++-- freud/order.py | 83 ++++++++++++++-------- freud/order/HexaticTranslational.cc | 23 +++--- freud/order/HexaticTranslational.h | 25 ++++--- freud/order/export-HexaticTranslational.cc | 25 +++---- 5 files changed, 99 insertions(+), 72 deletions(-) diff --git a/freud/CMakeLists.txt b/freud/CMakeLists.txt index aefe4d217..4af7371d0 100644 --- a/freud/CMakeLists.txt +++ b/freud/CMakeLists.txt @@ -174,11 +174,16 @@ target_set_install_rpath(_locality) # 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 -) +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) diff --git a/freud/order.py b/freud/order.py index 0aa5e55dd..89533e958 100644 --- a/freud/order.py +++ b/freud/order.py @@ -1,6 +1,6 @@ # 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-> -# ruff: noqa: E741 +# This file is from the freud project, released under the BSD 3-Clause License. + r""" The :class:`freud.order` module contains functions which compute order parameters for the whole system or individual particles. Order parameters take @@ -41,6 +41,7 @@ # _always_ do that, or you will have segfaults # np.import_array() + 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. @@ -64,7 +65,9 @@ def __init__(self, t_initial, t_final, scale, n_replicates=1, seed=None): if seed is None: seed = int(time.time()) - self._cpp_obj = freud._order.Cubatic(t_initial, t_final, scale, n_replicates, seed) + 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. @@ -282,7 +285,7 @@ class Hexatic(_PairCompute): """ def __init__(self, k=6, weighted=False): - self._cpp_obj= freud._order.Hexatic(k, weighted) + self._cpp_obj = freud._order.Hexatic(k, weighted) def compute(self, system, neighbors=None): r"""Calculates the hexatic order parameter. @@ -308,10 +311,11 @@ def compute(self, system, neighbors=None): `query arguments `_ (Default value: None). - """ # noqa: E501 + """ # noqa: E501 - nq, nlist, qargs, l_query_points, num_query_points = \ - self._preprocess_arguments(system, neighbors=neighbors) + 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 @@ -339,7 +343,8 @@ def weighted(self): def __repr__(self): return "freud.order.{cls}(k={k}, weighted={weighted})".format( - cls=type(self).__name__, k=self.k, weighted=self.weighted) + cls=type(self).__name__, k=self.k, weighted=self.weighted + ) def plot(self, ax=None): """Plot order parameter distribution. @@ -353,20 +358,23 @@ 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 @@ -490,10 +498,9 @@ def __init__(self, l, average=False, wl=False, weighted=False, wl_normalize=Fals if not isinstance(l, collections.abc.Sequence): l = [l] if len(l) == 0: - raise ValueError("At least one l must be specified.") # noqa: EM101 + raise ValueError("At least one l must be specified.") # noqa: EM101 self._cpp_obj = freud._order.Steinhardt(l, average, wl, weighted, wl_normalize) - @property def average(self): """bool: Whether the averaged Steinhardt order parameter was @@ -581,15 +588,19 @@ def compute(self, system, neighbors=None): """ # Call the pair compute setup function - nq, nlist, qargs, l_query_points, num_query_points = self._preprocess_arguments(system, neighbors=neighbors) + 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 (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})") + 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. @@ -610,11 +621,12 @@ def plot(self, ax=None): legend_labels = [ ( - fr"${'w' if self.wl else 'q'}{'\'' if self.weighted else ''}_" + rf"${'w' if self.wl else 'q'}{'\'' if self.weighted else ''}_" f"{{{sph_l}{',ave' if self.average else ''}}}$" - ) for sph_l in ls + ) + 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: @@ -626,11 +638,13 @@ 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 @@ -677,14 +691,15 @@ class SolidLiquid(_PairCompute): def __init__(self, l, q_threshold, solid_threshold, normalize_q=True): if not isinstance(solid_threshold, int): - warning_text = ( + 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) - + self._cpp_obj = freud._order.SolidLiquid( + l, q_threshold, solid_threshold, normalize_q + ) def compute(self, system, neighbors=None): r"""Compute the order parameter. @@ -701,8 +716,9 @@ def compute(self, system, neighbors=None): (Default value: None). """ - nq, nlist, qargs, l_query_points, num_query_points = \ - self._preprocess_arguments(system, neighbors=neighbors) + 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 @@ -793,17 +809,20 @@ 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 @@ -929,10 +948,13 @@ class ContinuousCoordination(_PairCompute): coordination number. (Default value: :code:`True`) """ + def __init__(self, powers=None, compute_log=True, compute_exp=True): if powers is None: powers = [2.0] - self._cpp_obj = freud._order.ContinuousCoordination(powers, compute_log, compute_exp) + 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. @@ -957,13 +979,14 @@ def compute(self, system=None, voronoi=None): (Default value: None). """ if system is None and voronoi is None: - raise ValueError("Must specify system or voronoi.") # noqa: EM101 + raise ValueError("Must specify system or voronoi.") # noqa: EM101 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.") # noqa: EM101 + "Must call compute on Voronoi object prior to computing coordination." + ) # noqa: EM101 cpp_voronoi = voronoi self._cpp_obj.compute(cpp_voronoi._cpp_obj) return self diff --git a/freud/order/HexaticTranslational.cc b/freud/order/HexaticTranslational.cc index 8a1217ae8..8d7cf10be 100644 --- a/freud/order/HexaticTranslational.cc +++ b/freud/order/HexaticTranslational.cc @@ -4,8 +4,8 @@ #include "HexaticTranslational.h" #include "ManagedArray.h" #include "NeighborList.h" -#include "NeighborQuery.h" #include "NeighborPerPointIterator.h" +#include "NeighborQuery.h" namespace freud { namespace order { @@ -14,17 +14,16 @@ template template void HexaticTranslational::computeGeneral(Func func, const std::shared_ptr nlist, const std::shared_ptr& points, - //const vec3* points, - const freud::locality::QueryArgs qargs, - bool normalize_by_k) + // const vec3* 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}); + // m_psi_array.prepare(Np); + m_psi_array = std::make_shared>>(std::vector {Np}); freud::locality::loopOverNeighborsIterator( points, points->getPoints(), Np, qargs, nlist, @@ -39,28 +38,28 @@ void HexaticTranslational::computeGeneral(Func func, const std::shared_ptr(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, +// void Hexatic::compute(const freud::locality::NeighborList* nlist, void Hexatic::compute(std::shared_ptr nlist, const std::shared_ptr& points, - //const vec3* points, + // const vec3* points, const freud::locality::QueryArgs& qargs) - //const freud::locality::NeighborQuery* points, +// const freud::locality::NeighborQuery* points, { computeGeneral( [this](const vec3& delta) { diff --git a/freud/order/HexaticTranslational.h b/freud/order/HexaticTranslational.h index 379ca9c95..55f95a2a6 100644 --- a/freud/order/HexaticTranslational.h +++ b/freud/order/HexaticTranslational.h @@ -51,15 +51,14 @@ template class HexaticTranslational //! Compute the order parameter template 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 vec3* points, + const std::shared_ptr& points, + const freud::locality::QueryArgs qargs, bool normalize_by_k); - //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 freud::locality::NeighborList* nlist, + // const freud::locality::NeighborQuery* points, + // freud::locality::QueryArgs qargs, + // bool normalize_by_k); const T m_k; //!< The symmetry order for Hexatic, or normalization for Translational const bool @@ -82,11 +81,11 @@ class Hexatic : public HexaticTranslational //! Compute the hexatic order parameter void compute(std::shared_ptr nlist, - const std::shared_ptr& points, - //const vec3* points, - const freud::locality::QueryArgs& qargs); - //void compute(const freud::locality::NeighborList* nlist, const freud::locality::NeighborQuery* points, - // freud::locality::QueryArgs qargs); + const std::shared_ptr& points, + // const vec3* points, + const freud::locality::QueryArgs& qargs); + // void compute(const freud::locality::NeighborList* nlist, const freud::locality::NeighborQuery* points, + // freud::locality::QueryArgs qargs); }; }; }; // end namespace freud::order diff --git a/freud/order/export-HexaticTranslational.cc b/freud/order/export-HexaticTranslational.cc index 9af165624..9244788c7 100644 --- a/freud/order/export-HexaticTranslational.cc +++ b/freud/order/export-HexaticTranslational.cc @@ -1,12 +1,15 @@ +// 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" -#include "HexaticTranslational.h" namespace freud { namespace order { @@ -15,31 +18,29 @@ using nb_array = nanobind::ndarray& self, - std::shared_ptr nlist, - std::shared_ptr& points, - const locality::QueryArgs& qargs - ) +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); + 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("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) - ; + .def("isWeighted", &HexaticTranslational::isWeighted); } } // namespace detail -}; }; // end namespace freud::pmft +}; }; // namespace freud::order From 52e089325197f7db41bc815a1effdeb1b314e239 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 4 Nov 2024 14:02:24 -0500 Subject: [PATCH 66/91] Lint order.py --- freud/order.py | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/freud/order.py b/freud/order.py index 89533e958..da4b52358 100644 --- a/freud/order.py +++ b/freud/order.py @@ -12,11 +12,8 @@ from math import floor -from freud.util import _Compute # , quat, vec3 +from freud.util import _Compute -from freud.errors import FreudDeprecationWarning - -# from cython.operator cimport dereference from freud.locality import _PairCompute @@ -28,12 +25,7 @@ import numpy as np import freud.locality - -import numpy as np - import freud._order -# cimport freud.locality -# cimport freud.util logger = logging.getLogger(__name__) @@ -190,7 +182,7 @@ def compute(self, orientations): """ # noqa: E501 if orientations.shape[1] == 4: raise ValueError( - "In freud versions >=3.0.0, Nematic.compute() takes " + "In freud versions >=3.0.0, Nematic.compute() takes " # noqa: EM101 "3d orientation vectors instead of 4d quaternions." ) orientations = freud.util._convert_array(orientations, shape=(None, 3)) @@ -200,6 +192,7 @@ def compute(self, orientations): "Including zero vector in the orientations array " "may lead to undefined behavior.", UserWarning, + stacklevel=2, ) self._cpp_obj.compute(orientations) @@ -311,7 +304,7 @@ def compute(self, system, neighbors=None): `query arguments `_ (Default value: None). - """ # noqa: E501 + """ nq, nlist, qargs, l_query_points, num_query_points = self._preprocess_arguments( system, neighbors=neighbors @@ -342,8 +335,9 @@ def weighted(self): 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): @@ -494,9 +488,9 @@ class Steinhardt(_PairCompute): of the Steinhardt order parameter (Default value = :code:`False`). """ - def __init__(self, l, average=False, wl=False, weighted=False, wl_normalize=False): + def __init__(self, l, average=False, wl=False, weighted=False, wl_normalize=False): # noqa: E741 if not isinstance(l, collections.abc.Sequence): - l = [l] + l = [l] # noqa: E741 if len(l) == 0: raise ValueError("At least one l must be specified.") # noqa: EM101 self._cpp_obj = freud._order.Steinhardt(l, average, wl, weighted, wl_normalize) @@ -689,7 +683,7 @@ class SolidLiquid(_PairCompute): :code:`True`). """ - def __init__(self, l, q_threshold, solid_threshold, normalize_q=True): + def __init__(self, l, q_threshold, solid_threshold, normalize_q=True): # noqa: E741 if not isinstance(solid_threshold, int): warning_text = ( "solid_threshold should be an integer, and will be rounded down" @@ -985,8 +979,8 @@ def compute(self, system=None, voronoi=None): voronoi.compute(system) elif not hasattr(voronoi, "nlist"): raise RuntimeError( - "Must call compute on Voronoi object prior to computing coordination." - ) # noqa: EM101 + "Must call compute on Voronoi object prior to computing coordination." # noqa: EM101 + ) cpp_voronoi = voronoi self._cpp_obj.compute(cpp_voronoi._cpp_obj) return self From 90cb85d0c2beaf2b65f0aa2cf990619ed87d8eae Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 4 Nov 2024 14:04:12 -0500 Subject: [PATCH 67/91] Lint import block --- freud/order.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/freud/order.py b/freud/order.py index da4b52358..f07d95100 100644 --- a/freud/order.py +++ b/freud/order.py @@ -10,22 +10,18 @@ Fourier Transforms. """ -from math import floor - -from freud.util import _Compute - - -from freud.locality import _PairCompute - import collections.abc import logging import time import warnings +from math import floor import numpy as np -import freud.locality import freud._order +import freud.locality +from freud.locality import _PairCompute +from freud.util import _Compute logger = logging.getLogger(__name__) From 687a28e4755e7bc93496e051a22c710196f65401 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 4 Nov 2024 14:07:16 -0500 Subject: [PATCH 68/91] Remove unused header --- freud/order/export-RotationalAutocorrelation.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/freud/order/export-RotationalAutocorrelation.cc b/freud/order/export-RotationalAutocorrelation.cc index a3ddc35e3..1ab4907d4 100644 --- a/freud/order/export-RotationalAutocorrelation.cc +++ b/freud/order/export-RotationalAutocorrelation.cc @@ -5,7 +5,6 @@ #include #include #include // NOLINT(misc-include-cleaner): used implicitly -#include // NOLINT(misc-include-cleaner): used implicitly #include #include "ManagedArray.h" From b88f132f70e3c027ad9d762457c23088cc7049e6 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 4 Nov 2024 14:17:23 -0500 Subject: [PATCH 69/91] Add missing header --- freud/order/Cubatic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/freud/order/Cubatic.h b/freud/order/Cubatic.h index 5e5d35b5f..aa7abd07a 100644 --- a/freud/order/Cubatic.h +++ b/freud/order/Cubatic.h @@ -7,6 +7,7 @@ #include "ManagedArray.h" #include "VectorMath.h" #include +#include #include /*! \file Cubatic.h From 1fc13ce4c9f7dbe6faf3af5c8c40c5b6206e19db Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 4 Nov 2024 14:22:30 -0500 Subject: [PATCH 70/91] Fix fstring issue --- freud/order.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freud/order.py b/freud/order.py index f07d95100..c4a489b81 100644 --- a/freud/order.py +++ b/freud/order.py @@ -609,9 +609,10 @@ def plot(self, ax=None): if not isinstance(ls, list): ls = [ls] + weighted_label = "'" if self.weighted else "" legend_labels = [ ( - rf"${'w' if self.wl else 'q'}{'\'' if self.weighted else ''}_" + rf"${'w' if self.wl else 'q'}{weighted_label}_" f"{{{sph_l}{',ave' if self.average else ''}}}$" ) for sph_l in ls From 34e2d5f5879c15868ed1f2e06b62fd94c155ac34 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 4 Nov 2024 14:23:30 -0500 Subject: [PATCH 71/91] Swap to proper submodule version --- doc/source/gettingstarted/examples | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/gettingstarted/examples b/doc/source/gettingstarted/examples index b135ea692..18c84895a 160000 --- a/doc/source/gettingstarted/examples +++ b/doc/source/gettingstarted/examples @@ -1 +1 @@ -Subproject commit b135ea69247308bd0a46f6a880f8c1944bf9a5f1 +Subproject commit 18c84895abf64ed5766957d8aa20322c3f18364b From 21bc88ffa8319421b4ba4558b42213b1498e38d8 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 4 Nov 2024 14:28:07 -0500 Subject: [PATCH 72/91] Fix fstring weirdness --- freud/order.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/freud/order.py b/freud/order.py index c4a489b81..9377ece76 100644 --- a/freud/order.py +++ b/freud/order.py @@ -609,10 +609,9 @@ def plot(self, ax=None): if not isinstance(ls, list): ls = [ls] - weighted_label = "'" if self.weighted else "" legend_labels = [ ( - rf"${'w' if self.wl else 'q'}{weighted_label}_" + f"${'w' if self.wl else 'q'}{'\'' if self.weighted else ''}_" f"{{{sph_l}{',ave' if self.average else ''}}}$" ) for sph_l in ls From 3fbec5657fcac684f409d859a28d937282b23ffb Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 4 Nov 2024 14:34:11 -0500 Subject: [PATCH 73/91] Fix CI scripts --- .github/workflows/release.yaml | 2 +- .github/workflows/test.yaml | 1 + freud/__init__.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) 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/freud/__init__.py b/freud/__init__.py index aabe5816f..93f7683ec 100644 --- a/freud/__init__.py +++ b/freud/__init__.py @@ -2,7 +2,7 @@ # This file is from the freud project, released under the BSD 3-Clause License. # cluster,; density,; diffraction,; environment,; interface,; msd,; -from . import box, data, locality, order, parallel, pmft +from . import box, data, interface, locality, order, parallel, pmft from .box import Box from .locality import AABBQuery, LinkCell, NeighborList from .parallel import NumThreads, get_num_threads, set_num_threads From e2809f28327dfb2af00df965e3bf2efcb491087f Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 4 Nov 2024 14:38:37 -0500 Subject: [PATCH 74/91] Fix fstring weirdness part 2 --- freud/order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freud/order.py b/freud/order.py index 9377ece76..06f13dd13 100644 --- a/freud/order.py +++ b/freud/order.py @@ -611,7 +611,7 @@ def plot(self, ax=None): legend_labels = [ ( - f"${'w' if self.wl else 'q'}{'\'' if self.weighted else ''}_" + f'${"w" if self.wl else "q"}{"'" if self.weighted else ""}_' f"{{{sph_l}{',ave' if self.average else ''}}}$" ) for sph_l in ls From ca7aac526020614da561dcd80bc333724d58edaa Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 4 Nov 2024 14:42:04 -0500 Subject: [PATCH 75/91] Fix missing imports --- freud/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freud/__init__.py b/freud/__init__.py index 93f7683ec..7694cf787 100644 --- a/freud/__init__.py +++ b/freud/__init__.py @@ -2,7 +2,7 @@ # This file is from the freud project, released under the BSD 3-Clause License. # cluster,; density,; diffraction,; environment,; interface,; msd,; -from . import box, data, interface, locality, order, parallel, pmft +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 @@ -23,7 +23,7 @@ # "environment", "interface", "locality", - # "msd", + "msd", "order", "parallel", "pmft", From c3f4d38b0d23a592369a7f0465969cecd23343f4 Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 5 Nov 2024 09:33:10 -0500 Subject: [PATCH 76/91] Fix fstring again --- freud/order.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freud/order.py b/freud/order.py index 06f13dd13..23866dec0 100644 --- a/freud/order.py +++ b/freud/order.py @@ -609,9 +609,10 @@ def plot(self, ax=None): if not isinstance(ls, list): ls = [ls] + weighted_str = "'" if self.weighted else "" legend_labels = [ ( - f'${"w" if self.wl else "q"}{"'" if self.weighted else ""}_' + f"${'w' if self.wl else 'q'}{weighted_str}_" f"{{{sph_l}{',ave' if self.average else ''}}}$" ) for sph_l in ls From 8bae101761f8cea717e4d5ce13ab216655f714c8 Mon Sep 17 00:00:00 2001 From: "Joshua A. Anderson" Date: Tue, 5 Nov 2024 10:49:31 -0500 Subject: [PATCH 77/91] Address clang-tidy violations. --- freud/locality/NeighborComputeFunctional.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freud/locality/NeighborComputeFunctional.h b/freud/locality/NeighborComputeFunctional.h index eb53ac7b2..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) + NeighborListPerPointIterator(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); @@ -236,7 +236,7 @@ void loopOverNeighbors(const std::shared_ptr& neighbor_query, con * input. It should implement iteration logic over the iterator. */ template -void loopOverNeighborListIterator(const std::shared_ptr nlist, const ComputePairType& cf, +void loopOverNeighborListIterator(const std::shared_ptr& nlist, const ComputePairType& cf, bool parallel = true) { util::forLoopWrapper( From f716ee238efcef7f0033e3b7d76b23e2d26b8280 Mon Sep 17 00:00:00 2001 From: "Joshua A. Anderson" Date: Tue, 5 Nov 2024 12:10:29 -0500 Subject: [PATCH 78/91] Apply suggestions from code review. --- freud/order.py | 4 ---- freud/order/Cubatic.cc | 8 ++------ freud/order/Cubatic.h | 1 - 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/freud/order.py b/freud/order.py index 23866dec0..6c559f2cd 100644 --- a/freud/order.py +++ b/freud/order.py @@ -25,10 +25,6 @@ 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() - class Cubatic(_Compute): r"""Compute the cubatic order parameter :cite:`Haji_Akbari_2015` for a system of diff --git a/freud/order/Cubatic.cc b/freud/order/Cubatic.cc index f2aa5f9fc..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,11 +71,7 @@ tensor4 tensor4::operator*(const float& b) const void tensor4::copyToManagedArray(util::ManagedArray& ma) { - // TODO: this may be possible with std::copy but this works as well. - for (unsigned int i = 0; i < 81; i++) - { - ma[i] = data[i]; - } + std::copy(data.begin(), data.end(), ma.data()); } //! Complete tensor contraction. diff --git a/freud/order/Cubatic.h b/freud/order/Cubatic.h index aa7abd07a..cef8ae784 100644 --- a/freud/order/Cubatic.h +++ b/freud/order/Cubatic.h @@ -8,7 +8,6 @@ #include "VectorMath.h" #include #include -#include /*! \file Cubatic.h \brief Compute the cubatic order parameter for each particle. From 7395311eda5529f3b4f7d2e699f6cf8eb71b368c Mon Sep 17 00:00:00 2001 From: Jen Bradley <55467578+janbridley@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:31:38 -0500 Subject: [PATCH 79/91] Update freud/order.py Co-authored-by: Joshua A. Anderson --- freud/order.py | 1 - 1 file changed, 1 deletion(-) diff --git a/freud/order.py b/freud/order.py index 6c559f2cd..37ffc131b 100644 --- a/freud/order.py +++ b/freud/order.py @@ -753,7 +753,6 @@ def particle_harmonics(self): 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 From 1ce2288db44887b1c4cd898ea34fcf6e5e2e103b Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 5 Nov 2024 12:34:37 -0500 Subject: [PATCH 80/91] Fix EM101 --- freud/order.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/freud/order.py b/freud/order.py index 37ffc131b..16c13a610 100644 --- a/freud/order.py +++ b/freud/order.py @@ -173,10 +173,13 @@ def compute(self, orientations): 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 " # noqa: EM101 + 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: @@ -484,7 +487,8 @@ def __init__(self, l, average=False, wl=False, weighted=False, wl_normalize=Fals if not isinstance(l, collections.abc.Sequence): l = [l] # noqa: E741 if len(l) == 0: - raise ValueError("At least one l must be specified.") # noqa: EM101 + msg = "At least one l must be specified." + raise ValueError(msg) self._cpp_obj = freud._order.Steinhardt(l, average, wl, weighted, wl_normalize) @property @@ -846,7 +850,8 @@ class RotationalAutocorrelation(_Compute): def __init__(self, l): # noqa: E741 if l % 2 or l < 0: - raise ValueError("The quantum number must be a positive, even integer.") # noqa: EM101 + 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): @@ -965,13 +970,15 @@ def compute(self, system=None, voronoi=None): (Default value: None). """ if system is None and voronoi is None: - raise ValueError("Must specify system or voronoi.") # noqa: EM101 + 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"): + msg = "Must call compute on Voronoi object prior to computing coordination." raise RuntimeError( - "Must call compute on Voronoi object prior to computing coordination." # noqa: EM101 + msg ) cpp_voronoi = voronoi self._cpp_obj.compute(cpp_voronoi._cpp_obj) From da307a4f9711e6e9d7bc5b7642efce4422b083bb Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 5 Nov 2024 12:36:31 -0500 Subject: [PATCH 81/91] Globally disable E741 --- .ruff.toml | 2 +- freud/order.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.ruff.toml b/.ruff.toml index 537d89298..9312beb35 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -26,6 +26,7 @@ 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" ] [lint.flake8-import-conventions] @@ -34,7 +35,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/order.py b/freud/order.py index 16c13a610..c0cff4af6 100644 --- a/freud/order.py +++ b/freud/order.py @@ -483,9 +483,9 @@ class Steinhardt(_PairCompute): of the Steinhardt order parameter (Default value = :code:`False`). """ - def __init__(self, l, average=False, wl=False, weighted=False, wl_normalize=False): # noqa: E741 + def __init__(self, l, average=False, wl=False, weighted=False, wl_normalize=False): if not isinstance(l, collections.abc.Sequence): - l = [l] # noqa: E741 + l = [l] if len(l) == 0: msg = "At least one l must be specified." raise ValueError(msg) @@ -680,7 +680,7 @@ class SolidLiquid(_PairCompute): :code:`True`). """ - def __init__(self, l, q_threshold, solid_threshold, normalize_q=True): # noqa: E741 + 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" @@ -848,7 +848,7 @@ class RotationalAutocorrelation(_Compute): integer. """ - def __init__(self, l): # noqa: E741 + def __init__(self, l): if l % 2 or l < 0: msg = "The quantum number must be a positive, even integer." raise ValueError(msg) From e8a6e8db41b7d4e8da5783c58b8309e72f6b94db Mon Sep 17 00:00:00 2001 From: Jen Bradley <55467578+janbridley@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:38:19 -0500 Subject: [PATCH 82/91] Update freud/order/Steinhardt.cc Co-authored-by: Joshua A. Anderson --- freud/order/Steinhardt.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index 3ce275ffb..b9baabbad 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -295,7 +295,6 @@ std::vector Steinhardt::normalizeSystem() { const auto wigner3j_values = getWigner3j(l); float wl_system_norm = reduceWigner3j(qlm.get()->data(), l, wigner3j_values); - // float wl_system_norm = reduceWigner3j(qlm.get(), l, wigner3j_values); // The normalization factor of wl is calculated using qli, which is // equivalent to calculate the normalization factor from qlmi From 1abbdaff754758371fd270d21c39b8246d44bc95 Mon Sep 17 00:00:00 2001 From: Jen Bradley <55467578+janbridley@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:38:32 -0500 Subject: [PATCH 83/91] Update freud/order/Steinhardt.cc Co-authored-by: Joshua A. Anderson --- freud/order/Steinhardt.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index b9baabbad..442037234 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -313,7 +313,6 @@ std::vector Steinhardt::normalizeSystem() return system_norms; } -// void Steinhardt::aggregatewl(util::ManagedArray& target, void Steinhardt::aggregatewl( std::shared_ptr>& target, const std::vector>>>& source, From 0491da2fed36f0181dd645d4b132680c5a1e3fc3 Mon Sep 17 00:00:00 2001 From: Jen Bradley <55467578+janbridley@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:38:46 -0500 Subject: [PATCH 84/91] Update freud/order/Wigner3j.cc Co-authored-by: Joshua A. Anderson --- freud/order/Wigner3j.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/freud/order/Wigner3j.cc b/freud/order/Wigner3j.cc index c2815aaf6..ea3674433 100644 --- a/freud/order/Wigner3j.cc +++ b/freud/order/Wigner3j.cc @@ -18,8 +18,6 @@ inline int lmIndex(int l, int m) } float reduceWigner3j(const std::complex* source, unsigned int l_, const std::vector& wigner3j) -// float reduceWigner3j(const util::ManagedArray>* source, unsigned int l_, const -// std::vector& wigner3j) { /* * Wigner 3j coefficients: From 83fa6c537905d2d426064aa27b9ac8699f4eff74 Mon Sep 17 00:00:00 2001 From: Jen Bradley <55467578+janbridley@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:39:03 -0500 Subject: [PATCH 85/91] Update freud/order/Steinhardt.h Co-authored-by: Joshua A. Anderson --- freud/order/Steinhardt.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/freud/order/Steinhardt.h b/freud/order/Steinhardt.h index a22087ead..9701259cf 100644 --- a/freud/order/Steinhardt.h +++ b/freud/order/Steinhardt.h @@ -206,8 +206,6 @@ class Steinhardt 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_qlmi; //!< qlm for each - // particle i std::vector>>> m_qlm; //!< Normalized qlm(Ave) for the whole system std::vector>> From b0dba0acf2315c95ec9854352be93e9fed45d015 Mon Sep 17 00:00:00 2001 From: Jen Bradley <55467578+janbridley@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:39:15 -0500 Subject: [PATCH 86/91] Update freud/order/Wigner3j.h Co-authored-by: Joshua A. Anderson --- freud/order/Wigner3j.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/freud/order/Wigner3j.h b/freud/order/Wigner3j.h index dd4521a45..c3b627c2d 100644 --- a/freud/order/Wigner3j.h +++ b/freud/order/Wigner3j.h @@ -24,8 +24,6 @@ int lmIndex(int l, int m); // third-order rotational invariant quantity. // source array must be indexed by m, like [0, 1, ..., l, -1, -2, ..., -l]. float reduceWigner3j(const std::complex* source, unsigned int l_, const std::vector& wigner3j); -// float reduceWigner3j(const util::ManagedArray>* source, unsigned int l_, const -// std::vector& wigner3j); std::vector getWigner3j(unsigned int l); // All Wigner 3j coefficients created using sympy From 4475fb98c6b5a66948abab54ac248359bae68f91 Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 5 Nov 2024 12:39:46 -0500 Subject: [PATCH 87/91] Run pre-commit --- freud/order.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/freud/order.py b/freud/order.py index c0cff4af6..b097d879d 100644 --- a/freud/order.py +++ b/freud/order.py @@ -177,9 +177,7 @@ def compute(self, orientations): "In freud versions >=3.0.0, Nematic.compute() takes " "3d orientation vectors instead of 4d quaternions." ) - raise ValueError( - msg - ) + raise ValueError(msg) orientations = freud.util._convert_array(orientations, shape=(None, 3)) if len(np.where(~orientations.any(axis=1))[0]) != 0: @@ -977,9 +975,7 @@ def compute(self, system=None, voronoi=None): voronoi.compute(system) elif not hasattr(voronoi, "nlist"): msg = "Must call compute on Voronoi object prior to computing coordination." - raise RuntimeError( - msg - ) + raise RuntimeError(msg) cpp_voronoi = voronoi self._cpp_obj.compute(cpp_voronoi._cpp_obj) return self From 6461db5ed014dc5ff050f6baeddcedcb4dcc531e Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 5 Nov 2024 15:07:31 -0500 Subject: [PATCH 88/91] Fix remaining lint errors --- .ruff.toml | 3 ++- freud/order.py | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.ruff.toml b/.ruff.toml index 9312beb35..5a2d00f42 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -26,7 +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" + "E741", + "E743" ] [lint.flake8-import-conventions] diff --git a/freud/order.py b/freud/order.py index b097d879d..732fbdccc 100644 --- a/freud/order.py +++ b/freud/order.py @@ -169,9 +169,10 @@ 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: msg = ( "In freud versions >=3.0.0, Nematic.compute() takes " @@ -511,7 +512,7 @@ def wl_normalize(self): return self._cpp_obj.isWlNormalized() @property - def l(self): # noqa: E743 + def l(self): """unsigned int: Spherical harmonic quantum number l.""" ls = self._cpp_obj.getL() return ls[0] if len(ls) == 1 else ls @@ -712,7 +713,7 @@ def compute(self, system, neighbors=None): return self @property - def l(self): # noqa: E743 + def l(self): """unsigned int: Spherical harmonic quantum number l.""" return self._cpp_obj.getL() @@ -879,7 +880,7 @@ def particle_order(self): 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._cpp_obj.getL() From a2058501fd538ea1439d3a38e1db4931d7795f13 Mon Sep 17 00:00:00 2001 From: janbridley Date: Thu, 7 Nov 2024 13:47:31 -0500 Subject: [PATCH 89/91] Fix docstring format --- freud/order.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freud/order.py b/freud/order.py index 732fbdccc..5bcd00f6e 100644 --- a/freud/order.py +++ b/freud/order.py @@ -157,7 +157,7 @@ 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:: @@ -170,7 +170,7 @@ def compute(self, orientations): Args: orientations \ - (:math:`\left(N_{particles}, 3 \right)` :class:`numpy.ndarray`): + (:math:`\\left(N_{particles}, 3 \\right)` :class:`numpy.ndarray`): Orientation vectors for which to calculate the order parameter. """ if orientations.shape[1] == 4: From 79f92e83d9b0d98d022bb55b30f82ee6ab1e3cad Mon Sep 17 00:00:00 2001 From: "Joshua A. Anderson" Date: Fri, 8 Nov 2024 08:47:11 -0500 Subject: [PATCH 90/91] Apply suggestions from code review --- freud/order.py | 4 ---- freud/order/HexaticTranslational.cc | 5 ----- freud/order/HexaticTranslational.h | 7 ------- freud/order/Nematic.cc | 2 -- freud/order/RotationalAutocorrelation.cc | 2 +- freud/order/SolidLiquid.cc | 3 --- freud/order/Steinhardt.cc | 3 --- 7 files changed, 1 insertion(+), 25 deletions(-) diff --git a/freud/order.py b/freud/order.py index 5bcd00f6e..8fffd3a0d 100644 --- a/freud/order.py +++ b/freud/order.py @@ -761,16 +761,12 @@ def cluster_sizes(self): @_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._cpp_obj.getNList()) - # nlist._compute = self - # return nlist return freud.locality._nlist_from_cnlist(self._cpp_obj.getNList()) @_Compute._computed_property diff --git a/freud/order/HexaticTranslational.cc b/freud/order/HexaticTranslational.cc index 8d7cf10be..c2080d10c 100644 --- a/freud/order/HexaticTranslational.cc +++ b/freud/order/HexaticTranslational.cc @@ -14,7 +14,6 @@ template template void HexaticTranslational::computeGeneral(Func func, const std::shared_ptr nlist, const std::shared_ptr& points, - // const vec3* points, const freud::locality::QueryArgs qargs, bool normalize_by_k) { const auto box = points->getBox(); @@ -22,7 +21,6 @@ void HexaticTranslational::computeGeneral(Func func, const std::shared_ptrgetNPoints(); - // m_psi_array.prepare(Np); m_psi_array = std::make_shared>>(std::vector {Np}); freud::locality::loopOverNeighborsIterator( @@ -54,12 +52,9 @@ void HexaticTranslational::computeGeneral(Func func, const std::shared_ptr(k, weighted) {} -// void Hexatic::compute(const freud::locality::NeighborList* nlist, void Hexatic::compute(std::shared_ptr nlist, const std::shared_ptr& points, - // const vec3* points, const freud::locality::QueryArgs& qargs) -// const freud::locality::NeighborQuery* points, { computeGeneral( [this](const vec3& delta) { diff --git a/freud/order/HexaticTranslational.h b/freud/order/HexaticTranslational.h index 55f95a2a6..56165c60f 100644 --- a/freud/order/HexaticTranslational.h +++ b/freud/order/HexaticTranslational.h @@ -55,10 +55,6 @@ template class HexaticTranslational const std::shared_ptr& points, const freud::locality::QueryArgs qargs, bool normalize_by_k); - // void computeGeneral(Func func, const freud::locality::NeighborList* nlist, - // const freud::locality::NeighborQuery* points, - // freud::locality::QueryArgs qargs, - // bool normalize_by_k); const T m_k; //!< The symmetry order for Hexatic, or normalization for Translational const bool @@ -82,10 +78,7 @@ class Hexatic : public HexaticTranslational void compute(std::shared_ptr nlist, const std::shared_ptr& points, - // const vec3* points, const freud::locality::QueryArgs& qargs); - // void compute(const freud::locality::NeighborList* nlist, const freud::locality::NeighborQuery* points, - // freud::locality::QueryArgs qargs); }; }; }; // end namespace freud::order diff --git a/freud/order/Nematic.cc b/freud/order/Nematic.cc index d4923cfd9..cc1e26d02 100644 --- a/freud/order/Nematic.cc +++ b/freud/order/Nematic.cc @@ -1,8 +1,6 @@ // 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 diff --git a/freud/order/RotationalAutocorrelation.cc b/freud/order/RotationalAutocorrelation.cc index c48fa4a91..a73093476 100644 --- a/freud/order/RotationalAutocorrelation.cc +++ b/freud/order/RotationalAutocorrelation.cc @@ -5,7 +5,7 @@ #include "utils.h" #include -// quat imported from VectorMath in rotationalautocorrelation.h +#include "VectorMath.h" /*! \file RotationalAutocorrelation.cc \brief Implements the RotationalAutocorrelation class. diff --git a/freud/order/SolidLiquid.cc b/freud/order/SolidLiquid.cc index 096041676..c1f793116 100644 --- a/freud/order/SolidLiquid.cc +++ b/freud/order/SolidLiquid.cc @@ -20,9 +20,6 @@ SolidLiquid::SolidLiquid(unsigned int l, float q_threshold, unsigned int solid_t } } -// void SolidLiquid::compute(const std::shared_ptr& nlist, -// const std::shared_ptr& points, -// freud::locality::QueryArgs& qargs) void SolidLiquid::compute(const std::shared_ptr& nlist, const std::shared_ptr& points, freud::locality::QueryArgs& qargs) diff --git a/freud/order/Steinhardt.cc b/freud/order/Steinhardt.cc index 442037234..1039ca1f3 100644 --- a/freud/order/Steinhardt.cc +++ b/freud/order/Steinhardt.cc @@ -54,15 +54,12 @@ void Steinhardt::reallocateArrays(unsigned int Np) 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_qlmi[l_index] = std::make_shared>>(std::vector {Np, num_ms}); - // m_qlm[l_index].prepare(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}); } From a0286f40ebab40ae9365ecde23b32b62feb7d6ef Mon Sep 17 00:00:00 2001 From: "Joshua A. Anderson" Date: Fri, 8 Nov 2024 08:56:57 -0500 Subject: [PATCH 91/91] pre-commit --- freud/order/HexaticTranslational.h | 1 - freud/order/RotationalAutocorrelation.cc | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/freud/order/HexaticTranslational.h b/freud/order/HexaticTranslational.h index 56165c60f..21d000896 100644 --- a/freud/order/HexaticTranslational.h +++ b/freud/order/HexaticTranslational.h @@ -55,7 +55,6 @@ template class HexaticTranslational 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) diff --git a/freud/order/RotationalAutocorrelation.cc b/freud/order/RotationalAutocorrelation.cc index a73093476..056c11bc0 100644 --- a/freud/order/RotationalAutocorrelation.cc +++ b/freud/order/RotationalAutocorrelation.cc @@ -3,9 +3,9 @@ #include "RotationalAutocorrelation.h" +#include "VectorMath.h" #include "utils.h" #include -#include "VectorMath.h" /*! \file RotationalAutocorrelation.cc \brief Implements the RotationalAutocorrelation class.