From 35ac9e5ecebc1a0ba5094c6ae345747d2db91e43 Mon Sep 17 00:00:00 2001 From: Kelly Wang Date: Tue, 25 May 2021 15:07:40 -0400 Subject: [PATCH 01/10] Precommit fixed. --- tests/test_environment_LocalDescriptors.py | 10 ++++++++++ tests/test_order_Hexatic.py | 10 ++++++++++ tests/test_order_SolidLiquid.py | 15 +++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/tests/test_environment_LocalDescriptors.py b/tests/test_environment_LocalDescriptors.py index ca9dd453e..4837088fe 100644 --- a/tests/test_environment_LocalDescriptors.py +++ b/tests/test_environment_LocalDescriptors.py @@ -438,3 +438,13 @@ def test_query_point_ne_points(self): "Failed for l={}, m={}, x={}, y = {}" "\ntheta={}, phi={}" ).format(l, m, scipy_val, ld_val, theta, phi) count += 1 + + def test_no_neighbors(self): + l_max = 8 + box = freud.box.Box.cube(10) + points = [(0, 0, 0)] + + ld = freud.environment.LocalDescriptors(l_max) + ld.compute((box, points), neighbors={"r_max": 1.25}) + assert ld.num_sphs == 0 + assert len(ld.sph) == 0 diff --git a/tests/test_order_Hexatic.py b/tests/test_order_Hexatic.py index 9eadcb118..9b0dea66b 100644 --- a/tests/test_order_Hexatic.py +++ b/tests/test_order_Hexatic.py @@ -218,3 +218,13 @@ def test_repr_png(self): hop._repr_png_() hop.plot() plt.close("all") + + def test_no_neighbors(self): + """Ensure that particles without neighbors are assigned NaN""" + box = freud.box.Box.square(10) + positions = [(0, 0, 0)] + hop = freud.order.Hexatic() + hop.compute((box, positions), neighbors={"r_max": 1.25}) + + assert np.all(np.isnan(hop.particle_order)) + npt.assert_allclose(np.nan_to_num(hop.particle_order), 0) diff --git a/tests/test_order_SolidLiquid.py b/tests/test_order_SolidLiquid.py index b087e7494..9093d38f1 100644 --- a/tests/test_order_SolidLiquid.py +++ b/tests/test_order_SolidLiquid.py @@ -1,4 +1,5 @@ import matplotlib +import numpy as np import numpy.testing as npt import pytest @@ -95,3 +96,17 @@ def test_attribute_access(self): def test_repr(self): comp = freud.order.SolidLiquid(6, q_threshold=0.7, solid_threshold=6) assert str(comp) == str(eval(repr(comp))) + + def test_no_neighbors(self): + """Ensure that particles without neighbors are assigned NaN""" + box = freud.box.Box.cube(10) + positions = [(0, 0, 0)] + comp = freud.order.SolidLiquid(6, q_threshold=0.7, solid_threshold=6) + comp.compute((box, positions), neighbors=dict(r_max=2.0)) + + npt.assert_equal(comp.cluster_idx, np.arange(len(comp.cluster_idx))) + npt.assert_equal(comp.cluster_sizes, np.ones_like(comp.cluster_sizes)) + assert comp.largest_cluster_size == 1 + npt.assert_equal(comp.num_connections, np.zeros_like(comp.cluster_sizes)) + assert np.all(np.isnan(comp.particle_harmonics)) + npt.assert_equal(comp.ql_ij, []) From a8f40bd6dc026f870e37b0685c67109c525a101b Mon Sep 17 00:00:00 2001 From: Kelly Wang Date: Tue, 25 May 2021 15:07:52 -0400 Subject: [PATCH 02/10] Updted docs. --- freud/order.pyx | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/freud/order.pyx b/freud/order.pyx index fc0a872ab..1760f8cc7 100644 --- a/freud/order.pyx +++ b/freud/order.pyx @@ -283,6 +283,15 @@ cdef class Hexatic(_PairCompute): **2D:** :class:`freud.order.Hexatic` is only defined for 2D systems. The points must be passed in as :code:`[x, y, 0]`. + .. 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, 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: k (unsigned int, optional): Symmetry of order parameter (Default value = :code:`6`). @@ -420,6 +429,10 @@ cdef class Translational(_PairCompute): .. note:: This class is slated for deprecation and will be removed in freud 3.0. + .. note:: + The value of per-particle order parameter will be set to 0 for + particles with no neighbors. + Args: k (float, optional): Normalization of order parameter (Default value = :code:`6.0`). @@ -628,7 +641,7 @@ cdef class Steinhardt(_PairCompute): @_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 + The raw array of :math:`\\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(), @@ -735,6 +748,15 @@ cdef class SolidLiquid(_PairCompute): the particle is considered solid-like. Finally, solid-like particles are clustered. + .. note:: + The value of :math:`q_l(i, j)` 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, it is possible to observe a value of 0 for the :math:`q_l(i, j)` + 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): Spherical harmonic quantum number l. @@ -827,7 +849,7 @@ cdef class SolidLiquid(_PairCompute): @_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 + The raw array of :math:`\\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(), From 4d454634376621287e586f0a53bb4f5bc75e029d Mon Sep 17 00:00:00 2001 From: Kelly Wang Date: Tue, 25 May 2021 15:43:48 -0400 Subject: [PATCH 03/10] SolidLiquid returns self. --- freud/order.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/freud/order.pyx b/freud/order.pyx index 1760f8cc7..6c93cfd4d 100644 --- a/freud/order.pyx +++ b/freud/order.pyx @@ -808,6 +808,7 @@ cdef class SolidLiquid(_PairCompute): self.thisptr.compute(nlist.get_ptr(), nq.get_ptr(), dereference(qargs.thisptr)) + return self @property def l(self): # noqa: E743 From 187673629bb9fe3bc82fb644dda3a2c9dec414cc Mon Sep 17 00:00:00 2001 From: Kelly Wang Date: Tue, 25 May 2021 15:44:45 -0400 Subject: [PATCH 04/10] Set Translation to equal nan when there are zero neighbors. --- cpp/order/HexaticTranslational.cc | 9 ++++++++- freud/order.pyx | 10 ++++++++-- tests/test_order_Translational.py | 6 ++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/cpp/order/HexaticTranslational.cc b/cpp/order/HexaticTranslational.cc index 0fe8abd42..1636de26d 100644 --- a/cpp/order/HexaticTranslational.cc +++ b/cpp/order/HexaticTranslational.cc @@ -37,7 +37,14 @@ void HexaticTranslational::computeGeneral(Func func, const freud::locality::N } if (normalize_by_k) { - m_psi_array[i] /= std::complex(m_k); + if (total_weight == 0.) + { + m_psi_array[i] /= std::complex(total_weight); + } + else + { + m_psi_array[i] /= std::complex(m_k); + } } else { diff --git a/freud/order.pyx b/freud/order.pyx index 6c93cfd4d..6c8cf5072 100644 --- a/freud/order.pyx +++ b/freud/order.pyx @@ -430,8 +430,14 @@ cdef class Translational(_PairCompute): This class is slated for deprecation and will be removed in freud 3.0. .. note:: - The value of per-particle order parameter will be set to 0 for - particles with no neighbors. + 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, 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: k (float, optional): diff --git a/tests/test_order_Translational.py b/tests/test_order_Translational.py index 8a571f43c..0f8975068 100644 --- a/tests/test_order_Translational.py +++ b/tests/test_order_Translational.py @@ -40,3 +40,9 @@ def test_repr(self): with pytest.warns(FreudDeprecationWarning): trans = freud.order.Translational(4) assert str(trans) == str(eval(repr(trans))) + + def test_no_neighbors(self): + box = freud.box.Box.square(10) + positions = [(0, 0, 0)] + trans = freud.order.Translational(4) + trans.compute((box, positions), neighbors={"r_max": 1.25}) From 382544299bf9c799ec3e8271a5d51c59553dc647 Mon Sep 17 00:00:00 2001 From: Kelly Wang Date: Tue, 14 Dec 2021 16:01:56 -0500 Subject: [PATCH 05/10] Fixed merge error and used std::nanf instead of divide by zero. --- cpp/order/HexaticTranslational.cc | 2 +- freud/order.pyx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/order/HexaticTranslational.cc b/cpp/order/HexaticTranslational.cc index 5b268cbe6..b968f5839 100644 --- a/cpp/order/HexaticTranslational.cc +++ b/cpp/order/HexaticTranslational.cc @@ -39,7 +39,7 @@ void HexaticTranslational::computeGeneral(Func func, const freud::locality::N { if (total_weight == 0.) { - m_psi_array[i] /= std::complex(total_weight); + m_psi_array[i]/= std::complex(std::nanf); } else { diff --git a/freud/order.pyx b/freud/order.pyx index 6cca51906..598b03c78 100644 --- a/freud/order.pyx +++ b/freud/order.pyx @@ -929,7 +929,7 @@ cdef class SolidLiquid(_PairCompute): @_Compute._computed_property def particle_harmonics(self): """:math:`\\left(N_{particles}, 2*l+1\\right)` :class:`numpy.ndarray`: - The raw array of :math:`\\overline{q}_{lm}(i)`. The array is provided in the + 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(), From eeebe361c03386ff508ce3e6f384eaae3e8411c1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 14 Dec 2021 21:16:04 +0000 Subject: [PATCH 06/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cpp/order/HexaticTranslational.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/order/HexaticTranslational.cc b/cpp/order/HexaticTranslational.cc index b968f5839..4eff29e94 100644 --- a/cpp/order/HexaticTranslational.cc +++ b/cpp/order/HexaticTranslational.cc @@ -39,7 +39,7 @@ void HexaticTranslational::computeGeneral(Func func, const freud::locality::N { if (total_weight == 0.) { - m_psi_array[i]/= std::complex(std::nanf); + m_psi_array[i] /= std::complex(std::nanf); } else { From e2e154e791735bdc0b74779f5f8ecd4ba1cdadd8 Mon Sep 17 00:00:00 2001 From: Kelly Wang Date: Tue, 14 Dec 2021 16:27:11 -0500 Subject: [PATCH 07/10] Updated changelog. --- ChangeLog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 79c263f44..d45c023e5 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -9,12 +9,14 @@ and this project adheres to ### Added * `freud.diffraction.StaticStructureFactorDirect` class (unstable) can be used to compute the static structure factor S(k) by sampling reciprocal space vectors. * Python 3.10 is supported. +* Hexatic, Translational, SolidLiquid, and LocalDescriptors handle cases with zero neighbors. ### Fixed * Added error checking for `r_min`, `r_max` arguments in `freud.density.RDF`, `freud.locality.NeighborList`, `freud.locality.NeighborQuery`, and `freud.density.LocalDensity` classes. * Doctests are now run with pytest. * Cleaned up tests for the static structure factor classes. * CMake build system only uses references to TBB target. +* Translational returns `nan` when particles do not have neighbors. ## v2.7.0 -- 2021-10-01 From 9c3ca16e1f6a9fe9370058c8adcc015e3c544c99 Mon Sep 17 00:00:00 2001 From: Kelly Wang Date: Tue, 14 Dec 2021 16:31:16 -0500 Subject: [PATCH 08/10] Set equal instead of /= --- cpp/order/HexaticTranslational.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/order/HexaticTranslational.cc b/cpp/order/HexaticTranslational.cc index b968f5839..36540847a 100644 --- a/cpp/order/HexaticTranslational.cc +++ b/cpp/order/HexaticTranslational.cc @@ -39,7 +39,7 @@ void HexaticTranslational::computeGeneral(Func func, const freud::locality::N { if (total_weight == 0.) { - m_psi_array[i]/= std::complex(std::nanf); + m_psi_array[i] = std::complex(std::nanf); } else { From 4ef574222ac8a57d8cac014a00d4894def95d7f4 Mon Sep 17 00:00:00 2001 From: Kelly Wang Date: Tue, 14 Dec 2021 16:59:09 -0500 Subject: [PATCH 09/10] Check if nan in translational no neighbor test and try without complex? --- cpp/order/HexaticTranslational.cc | 2 +- tests/test_order_Translational.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cpp/order/HexaticTranslational.cc b/cpp/order/HexaticTranslational.cc index 36540847a..857bf8b79 100644 --- a/cpp/order/HexaticTranslational.cc +++ b/cpp/order/HexaticTranslational.cc @@ -39,7 +39,7 @@ void HexaticTranslational::computeGeneral(Func func, const freud::locality::N { if (total_weight == 0.) { - m_psi_array[i] = std::complex(std::nanf); + m_psi_array[i] = std::nanf; } else { diff --git a/tests/test_order_Translational.py b/tests/test_order_Translational.py index 0f8975068..927a1bb17 100644 --- a/tests/test_order_Translational.py +++ b/tests/test_order_Translational.py @@ -45,4 +45,7 @@ def test_no_neighbors(self): box = freud.box.Box.square(10) positions = [(0, 0, 0)] trans = freud.order.Translational(4) - trans.compute((box, positions), neighbors={"r_max": 1.25}) + trans.compute((box, positions), neighbors={"r_max": 0.25}) + + assert np.all(np.isnan(trans.particle_order)) + npt.assert_allclose(np.nan_to_num(trans.particle_order), 0) From 3c152aea6b6375eeeb8afd26248128323ba3881b Mon Sep 17 00:00:00 2001 From: Kelly Wang Date: Tue, 14 Dec 2021 17:26:45 -0500 Subject: [PATCH 10/10] Revert to divide by zero. --- cpp/order/HexaticTranslational.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/order/HexaticTranslational.cc b/cpp/order/HexaticTranslational.cc index 857bf8b79..cb53abbc1 100644 --- a/cpp/order/HexaticTranslational.cc +++ b/cpp/order/HexaticTranslational.cc @@ -39,7 +39,7 @@ void HexaticTranslational::computeGeneral(Func func, const freud::locality::N { if (total_weight == 0.) { - m_psi_array[i] = std::nanf; + m_psi_array[i] /= std::complex(0.); } else {