From 0d6f9752278e2693c959c8ea6cdd268e4cf23794 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Mon, 26 Feb 2024 19:36:01 -0500 Subject: [PATCH 01/37] add from lattice_vectors, and to and from lenghts and angles --- freud/data.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/freud/data.py b/freud/data.py index 87c0c0bfb..978958f55 100644 --- a/freud/data.py +++ b/freud/data.py @@ -233,6 +233,21 @@ def hex(cls): fractions = np.array([[0, 0, 0], [0.5, 0.5, 0]]) return cls([1, np.sqrt(3)], fractions) + @classmethod + def from_lattice_vectors(cls, lattice_vectors, unique_positions): + """Create a unit cell from lattice vectors. + + Args: + lattice_vectors (:math:`(3, 3)` :class:`np.ndarray + The lattice vectors. Lattice vector a1 is lattice_vectors[:, 0], etc. + unique_positions (:math:`(N_{points}, 3)` :class:`np.ndarray`): + The basis positions. + + Returns: + :class:`~.UnitCell`: A unit cell with the given lattice vectors. + """ + return cls(freud.box.Box.from_lattice_vectors(lattice_vectors), unique_positions) + def make_random_system(box_size, num_points, is2D=False, seed=None): r"""Helper function to make random points with a cubic or square box. From 0717da15a9725ad513c0444d877e12fa07f98c43 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Mon, 26 Feb 2024 19:36:22 -0500 Subject: [PATCH 02/37] Add methods to convert box dimensions and angles --- freud/box.pyx | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/freud/box.pyx b/freud/box.pyx index e6203fa26..395420045 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -689,6 +689,17 @@ cdef class Box: [0, self.Ly, self.yz * self.Lz], [0, 0, self.Lz]]) + def to_box_lengths_and_angles(self): + r"""Return the box lengths and angles. + + Returns: + tuple: The box lengths and angles in radians + """ + alpha = np.arccos((self.xy*self.xz+self.yz)/(np.sqrt(1+self.xy**2)*np.sqrt(1+self.xz**2+self.yz**2))) + beta = np.arccos(self.xz/np.sqrt(1+self.xz**2+self.yz**2)) + gamma = np.arccos(self.xy/np.sqrt(1+self.xy**2)) + return (self.Lx, self.Ly, self.Lz, alpha, beta, gamma) + def __repr__(self): return ("freud.box.{cls}(Lx={Lx}, Ly={Ly}, Lz={Lz}, " "xy={xy}, xz={xz}, yz={yz}, " @@ -921,6 +932,65 @@ cdef class Box: "positional argument: L") return cls(Lx=L, Ly=L, Lz=0, xy=0, xz=0, yz=0, is2D=True) + @classmethod + def from_lattice_vectors(cls, lattice_vectors, dimensions=None): + """Create a unit cell from lattice vectors. + + Args: + lattice_vectors (:math:`(3, 3)` :class:`np.ndarray + The lattice vectors. Lattice vector a1 is lattice_vectors[:, 0], etc. + dimensions (int): The number of dimensions (Default value = :code:`None`) + + Returns: + :class:`~.UnitCell`: A unit cell with the given lattice vectors. + """ + lattice_matrix = np.asarray(lattice_vectors, dtype=np.float32) + v0 = lattice_matrix[:, 0] + v1 = lattice_matrix[:, 1] + v2 = lattice_matrix[:, 2] + Lx = np.sqrt(np.dot(v0, v0)) + a2x = np.dot(v0, v1) / Lx + Ly = np.sqrt(np.dot(v1, v1) - a2x * a2x) + xy = a2x / Ly + v0xv1 = np.cross(v0, v1) + v0xv1mag = np.sqrt(np.dot(v0xv1, v0xv1)) + Lz = np.dot(v2, v0xv1) / v0xv1mag + if Lz != 0: + a3x = np.dot(v0, v2) / Lx + xz = a3x / Lz + yz = (np.dot(v1, v2) - a2x * a3x) / (Ly * Lz) + else: + xz = yz = 0 + if dimensions is None: + dimensions = 2 if Lz == 0 else 3 + return cls.from_box([Lx, Ly, Lz, xy, xz, yz], dimensions=dimensions) + + @classmethod + def from_box_lengths_and_angles(cls, Lx, Ly, Lz, alpha, beta, gamma, dimensions=None): + r"""Construct a box from lengths and angles. + + Args: + Lx (float): The length in the x direction + Ly (float): The length in the y direction + Lz (float): The length in the z direction + alpha (float): The angle between the y and z axes in radians + beta (float): The angle between the x and z axes in radians + gamma (float): The angle between the x and y axes in radians + dimensions (int): The number of dimensions (Default value = :code:`None`) + + Returns: + :class:`freud.box.Box`: The resulting box object. + """ + a1 = np.array([Lx, 0, 0]) + a2 = np.array([Ly * np.cos(gamma), Ly * np.sin(gamma), 0]) + a3x = np.cos(beta) + a3y = (np.cos(alpha) - np.cos(beta) * np.cos(gamma)) / np.sin(gamma) + a3z = np.sqrt(1 - a3x**2 - a3y**2) + a3 = np.array([Lz * a3x, Lz * a3y, Lz * a3z]) + if dimensions is None: + dimensions = 2 if Lz == 0 else 3 + return cls.from_lattice_vectors(np.array([a1, a2, a3]).T, dimensions=dimensions) + cdef BoxFromCPP(const freud._box.Box & cppbox): b = Box(cppbox.getLx(), cppbox.getLy(), cppbox.getLz(), From 5a388bf922ccfc666036b2f7cc65dffbf679a2a0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:22:37 +0000 Subject: [PATCH 03/37] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- freud/data.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/freud/data.py b/freud/data.py index 978958f55..4e39414f5 100644 --- a/freud/data.py +++ b/freud/data.py @@ -246,7 +246,9 @@ def from_lattice_vectors(cls, lattice_vectors, unique_positions): Returns: :class:`~.UnitCell`: A unit cell with the given lattice vectors. """ - return cls(freud.box.Box.from_lattice_vectors(lattice_vectors), unique_positions) + return cls( + freud.box.Box.from_lattice_vectors(lattice_vectors), unique_positions + ) def make_random_system(box_size, num_points, is2D=False, seed=None): From 58a56f63237da9b6add42f7cc93f04a57159e4ce Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Wed, 20 Mar 2024 11:56:09 +0100 Subject: [PATCH 04/37] add type hints and a class method from box lengths and angles for unit cell class --- freud/data.py | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/freud/data.py b/freud/data.py index 978958f55..98624d34d 100644 --- a/freud/data.py +++ b/freud/data.py @@ -234,7 +234,9 @@ def hex(cls): return cls([1, np.sqrt(3)], fractions) @classmethod - def from_lattice_vectors(cls, lattice_vectors, unique_positions): + def from_lattice_vectors( + cls, lattice_vectors: np.ndarray, unique_positions: np.ndarray + ): """Create a unit cell from lattice vectors. Args: @@ -246,7 +248,40 @@ def from_lattice_vectors(cls, lattice_vectors, unique_positions): Returns: :class:`~.UnitCell`: A unit cell with the given lattice vectors. """ - return cls(freud.box.Box.from_lattice_vectors(lattice_vectors), unique_positions) + return cls( + freud.box.Box.from_lattice_vectors(lattice_vectors), unique_positions + ) + + @classmethod + def from_box_lengths_and_angles( + cls, + Lx: float, + Ly: float, + Lz: float, + alpha: float, + beta: float, + gamma: float, + unique_positions: np.ndarray, + ): + """Create a unit cell from box lengths and angles. + + Args: + Lx (float): The length of the box in the x direction. + Ly (float): The length of the box in the y direction. + Lz (float): The length of the box in the z direction. + alpha (float): The angle between the x and y lattice vectors. + beta (float): The angle between the x and z lattice vectors. + gamma (float): The angle between the y and z lattice vectors. + unique_positions (:math:`(N_{points}, 3)` :class:`np.ndarray + The basis positions. + + Returns: + :class:`~.UnitCell`: A unit cell with the given box lengths and angles. + """ + return cls( + freud.box.Box.from_box_lengths_and_angles(Lx, Ly, Lz, alpha, beta, gamma), + unique_positions, + ) def make_random_system(box_size, num_points, is2D=False, seed=None): From 746b923269152c1b7892dcca1b7d254855c2b993 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Wed, 20 Mar 2024 11:56:18 +0100 Subject: [PATCH 05/37] add type hints --- freud/box.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freud/box.pyx b/freud/box.pyx index 395420045..0662b9a5e 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -933,7 +933,7 @@ cdef class Box: return cls(Lx=L, Ly=L, Lz=0, xy=0, xz=0, yz=0, is2D=True) @classmethod - def from_lattice_vectors(cls, lattice_vectors, dimensions=None): + def from_lattice_vectors(cls, lattice_vectors:np.ndarray, dimensions:int=None): """Create a unit cell from lattice vectors. Args: @@ -966,7 +966,7 @@ cdef class Box: return cls.from_box([Lx, Ly, Lz, xy, xz, yz], dimensions=dimensions) @classmethod - def from_box_lengths_and_angles(cls, Lx, Ly, Lz, alpha, beta, gamma, dimensions=None): + def from_box_lengths_and_angles(cls, Lx:float, Ly:float, Lz:float, alpha:float, beta:float, gamma:float, dimensions:int=None): r"""Construct a box from lengths and angles. Args: From c7ad750988fae1864af29d88d36673d22ba5c567 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 11:04:52 +0000 Subject: [PATCH 06/37] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- freud/data.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/freud/data.py b/freud/data.py index 3d249e25a..98624d34d 100644 --- a/freud/data.py +++ b/freud/data.py @@ -249,9 +249,7 @@ def from_lattice_vectors( :class:`~.UnitCell`: A unit cell with the given lattice vectors. """ return cls( - freud.box.Box.from_lattice_vectors(lattice_vectors), unique_positions - ) @classmethod From c399ff32bc47a688ba037f285e8ed34df6f26bfb Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Wed, 20 Mar 2024 13:01:42 +0100 Subject: [PATCH 07/37] fix format --- freud/box.pyx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freud/box.pyx b/freud/box.pyx index 0662b9a5e..85f8c7400 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -933,7 +933,7 @@ cdef class Box: return cls(Lx=L, Ly=L, Lz=0, xy=0, xz=0, yz=0, is2D=True) @classmethod - def from_lattice_vectors(cls, lattice_vectors:np.ndarray, dimensions:int=None): + def from_lattice_vectors(cls, lattice_vectors: np.ndarray, dimensions: int = None): """Create a unit cell from lattice vectors. Args: @@ -966,7 +966,8 @@ cdef class Box: return cls.from_box([Lx, Ly, Lz, xy, xz, yz], dimensions=dimensions) @classmethod - def from_box_lengths_and_angles(cls, Lx:float, Ly:float, Lz:float, alpha:float, beta:float, gamma:float, dimensions:int=None): + def from_box_lengths_and_angles(cls, Lx: float, Ly: float, Lz: float, alpha: float, + beta: float, gamma: float, dimensions: int = None): r"""Construct a box from lengths and angles. Args: From 2a995326841ae01638f499f613d5e6473e3d91e8 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Wed, 20 Mar 2024 13:09:49 +0100 Subject: [PATCH 08/37] more formatting fixes --- freud/box.pyx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/freud/box.pyx b/freud/box.pyx index 85f8c7400..34e63997e 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -695,7 +695,10 @@ cdef class Box: Returns: tuple: The box lengths and angles in radians """ - alpha = np.arccos((self.xy*self.xz+self.yz)/(np.sqrt(1+self.xy**2)*np.sqrt(1+self.xz**2+self.yz**2))) + alpha = np.arccos( + (self.xy * self.xz + self.yz) + / (np.sqrt(1 + self.xy**2) * np.sqrt(1 + self.xz**2 + self.yz**2)) + ) beta = np.arccos(self.xz/np.sqrt(1+self.xz**2+self.yz**2)) gamma = np.arccos(self.xy/np.sqrt(1+self.xy**2)) return (self.Lx, self.Ly, self.Lz, alpha, beta, gamma) @@ -966,8 +969,16 @@ cdef class Box: return cls.from_box([Lx, Ly, Lz, xy, xz, yz], dimensions=dimensions) @classmethod - def from_box_lengths_and_angles(cls, Lx: float, Ly: float, Lz: float, alpha: float, - beta: float, gamma: float, dimensions: int = None): + def from_box_lengths_and_angles( + cls, + Lx: float, + Ly: float, + Lz: float, + alpha: float, + beta: float, + gamma: float, + dimensions: int = None, + ): r"""Construct a box from lengths and angles. Args: From 16d028a94fc5d257d36eadffe82d49a0ef9adc00 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Wed, 20 Mar 2024 14:29:59 +0100 Subject: [PATCH 09/37] fixes to logic and docs to all new functions --- freud/box.pyx | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/freud/box.pyx b/freud/box.pyx index 34e63997e..4d8c0a8a9 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -693,7 +693,7 @@ cdef class Box: r"""Return the box lengths and angles. Returns: - tuple: The box lengths and angles in radians + tuple: The box vector lengths and angles in radians """ alpha = np.arccos( (self.xy * self.xz + self.yz) @@ -701,7 +701,12 @@ cdef class Box: ) beta = np.arccos(self.xz/np.sqrt(1+self.xz**2+self.yz**2)) gamma = np.arccos(self.xy/np.sqrt(1+self.xy**2)) - return (self.Lx, self.Ly, self.Lz, alpha, beta, gamma) + L1 = self.Lx + a2 = [self.Ly*self.xy, self.Ly,0] + a3 = [self.Lz*self.xz, self.Lz*self.yz, self.Lz] + L2 = np.linalg.norm(a2) + L3 = np.linalg.norm(a3) + return (L1, L2, L3, alpha, beta, gamma) def __repr__(self): return ("freud.box.{cls}(Lx={Lx}, Ly={Ly}, Lz={Lz}, " @@ -936,21 +941,22 @@ cdef class Box: return cls(Lx=L, Ly=L, Lz=0, xy=0, xz=0, yz=0, is2D=True) @classmethod - def from_lattice_vectors(cls, lattice_vectors: np.ndarray, dimensions: int = None): + def from_lattice_vectors(cls, lattice_vectors, dimensions: int = None): """Create a unit cell from lattice vectors. Args: - lattice_vectors (:math:`(3, 3)` :class:`np.ndarray - The lattice vectors. Lattice vector a1 is lattice_vectors[:, 0], etc. + lattice_vectors (array-like): + The lattice vectors. The dimensions of the object should be 3x3. Lattice + vector a1 is lattice_vectors[:, 0], etc. dimensions (int): The number of dimensions (Default value = :code:`None`) Returns: :class:`~.UnitCell`: A unit cell with the given lattice vectors. """ lattice_matrix = np.asarray(lattice_vectors, dtype=np.float32) - v0 = lattice_matrix[:, 0] - v1 = lattice_matrix[:, 1] - v2 = lattice_matrix[:, 2] + v0 = lattice_matrix[0] + v1 = lattice_matrix[1] + v2 = lattice_matrix[2] Lx = np.sqrt(np.dot(v0, v0)) a2x = np.dot(v0, v1) / Lx Ly = np.sqrt(np.dot(v1, v1) - a2x * a2x) @@ -971,9 +977,9 @@ cdef class Box: @classmethod def from_box_lengths_and_angles( cls, - Lx: float, - Ly: float, - Lz: float, + L1: float, + L2: float, + L3: float, alpha: float, beta: float, gamma: float, @@ -982,9 +988,9 @@ cdef class Box: r"""Construct a box from lengths and angles. Args: - Lx (float): The length in the x direction - Ly (float): The length in the y direction - Lz (float): The length in the z direction + L1 (float): The length of the first lattice vector + L2 (float): The length of the second lattice vector + L3 (float): The length of the third lattice vector alpha (float): The angle between the y and z axes in radians beta (float): The angle between the x and z axes in radians gamma (float): The angle between the x and y axes in radians @@ -993,15 +999,15 @@ cdef class Box: Returns: :class:`freud.box.Box`: The resulting box object. """ - a1 = np.array([Lx, 0, 0]) - a2 = np.array([Ly * np.cos(gamma), Ly * np.sin(gamma), 0]) + a1 = np.array([L1, 0, 0]) + a2 = np.array([L2 * np.cos(gamma), L2 * np.sin(gamma), 0]) a3x = np.cos(beta) a3y = (np.cos(alpha) - np.cos(beta) * np.cos(gamma)) / np.sin(gamma) a3z = np.sqrt(1 - a3x**2 - a3y**2) - a3 = np.array([Lz * a3x, Lz * a3y, Lz * a3z]) + a3 = np.array([L3 * a3x, L3 * a3y, L3 * a3z]) if dimensions is None: - dimensions = 2 if Lz == 0 else 3 - return cls.from_lattice_vectors(np.array([a1, a2, a3]).T, dimensions=dimensions) + dimensions = 2 if L3 == 0 else 3 + return cls.from_lattice_vectors(np.array([a1, a2, a3]), dimensions=dimensions) cdef BoxFromCPP(const freud._box.Box & cppbox): From dc74488edffca77898d46001d433677dcc73e4c7 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Wed, 20 Mar 2024 14:30:09 +0100 Subject: [PATCH 10/37] add tests --- tests/test_box_Box.py | 57 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/test_box_Box.py b/tests/test_box_Box.py index 1b6daff5e..a613ffdf1 100644 --- a/tests/test_box_Box.py +++ b/tests/test_box_Box.py @@ -485,6 +485,63 @@ def test_from_box(self): box7 = freud.box.Box.from_matrix(box.to_matrix()) assert np.isclose(box.to_matrix(), box7.to_matrix()).all() + def test_standard_orthogonal_box(self): + box = freud.box.Box.from_box((1, 2, 3, 0, 0, 0)) + Lx, Ly, Lz, alpha, beta, gamma = box.to_box_lengths_and_angles() + npt.assert_allclose( + (Lx, Ly, Lz, alpha, beta, gamma), (1, 2, 3, np.pi / 2, np.pi / 2, np.pi / 2) + ) + + def test_to_and_from_box_lengths_and_angles(self): + original_box_lengths_and_angles = ( + 1, + 2, + 3, + np.pi / 3, + np.pi / 2.25, + np.pi / 2.35, + ) + box = freud.box.Box.from_box_lengths_and_angles( + *original_box_lengths_and_angles + ) + lengths_and_angles_computed = box.to_box_lengths_and_angles() + assert np.allclose(lengths_and_angles_computed, original_box_lengths_and_angles) + + def test_from_lattice_vectors_cubic_lattice(self): + lattice_vectors = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + unit_cell = freud.box.Box.from_lattice_vectors(lattice_vectors) + assert unit_cell.Lx == 1 + assert unit_cell.Ly == 1 + assert unit_cell.Lz == 1 + assert unit_cell.xy == 0 + assert unit_cell.xz == 0 + assert unit_cell.yz == 0 + + def test_from_lattice_vectors_non_cubic_lattice(self): + box = freud.box.Box.from_lattice_vectors([[1, 0, 0], [0, 2, 0], [0, 0, 3]]) + assert box.Lx == 1 + assert box.Ly == 2 + assert box.Lz == 3 + assert box.xy == 0 + assert box.xz == 0 + assert box.yz == 0 + + def test_tilted_lattice(self): + lattice_vectors = np.array([[1, 0, 0], [0.5, np.sqrt(3) / 2, 0], [0, 0, 1]]) + unit_cell = freud.box.Box.from_lattice_vectors(lattice_vectors) + assert np.isclose(unit_cell.Lx, 1.0) + assert np.isclose(unit_cell.Ly, np.sqrt(3) / 2) + assert np.isclose(unit_cell.Lz, 1.0) + assert np.isclose(unit_cell.xy, np.sqrt(3) / 3) + assert np.isclose(unit_cell.xz, 0.0) + assert np.isclose(unit_cell.yz, 0.0) + + def test_dimensions(self): + lattice_vectors = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 0]]) + unit_cell = freud.box.Box.from_lattice_vectors(lattice_vectors, dimensions=2) + assert unit_cell.dimensions == 2 + assert np.isclose(unit_cell.Lz, 0) + def test_matrix(self): box = freud.box.Box(2, 2, 2, 1, 0.5, 0.1) box2 = freud.box.Box.from_matrix(box.to_matrix()) From 7706c2f4ccc428a8d79cccd8ac6244ab85cc5bd3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:35:46 +0000 Subject: [PATCH 11/37] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- freud/box.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freud/box.pyx b/freud/box.pyx index 4d8c0a8a9..e89778b75 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -702,7 +702,7 @@ cdef class Box: beta = np.arccos(self.xz/np.sqrt(1+self.xz**2+self.yz**2)) gamma = np.arccos(self.xy/np.sqrt(1+self.xy**2)) L1 = self.Lx - a2 = [self.Ly*self.xy, self.Ly,0] + a2 = [self.Ly*self.xy, self.Ly,0] a3 = [self.Lz*self.xz, self.Lz*self.yz, self.Lz] L2 = np.linalg.norm(a2) L3 = np.linalg.norm(a3) @@ -947,7 +947,7 @@ cdef class Box: Args: lattice_vectors (array-like): The lattice vectors. The dimensions of the object should be 3x3. Lattice - vector a1 is lattice_vectors[:, 0], etc. + vector a1 is lattice_vectors[:, 0], etc. dimensions (int): The number of dimensions (Default value = :code:`None`) Returns: From 88609618cc560880ba32ddc9d60bb3877a101e83 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Wed, 20 Mar 2024 14:36:09 +0100 Subject: [PATCH 12/37] update some docstrings for consistency --- freud/box.pyx | 2 +- freud/data.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/freud/box.pyx b/freud/box.pyx index 4d8c0a8a9..9443548e2 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -947,7 +947,7 @@ cdef class Box: Args: lattice_vectors (array-like): The lattice vectors. The dimensions of the object should be 3x3. Lattice - vector a1 is lattice_vectors[:, 0], etc. + vector a1 is lattice_vectors[0], etc. dimensions (int): The number of dimensions (Default value = :code:`None`) Returns: diff --git a/freud/data.py b/freud/data.py index 98624d34d..aa5261c2a 100644 --- a/freud/data.py +++ b/freud/data.py @@ -241,7 +241,7 @@ def from_lattice_vectors( Args: lattice_vectors (:math:`(3, 3)` :class:`np.ndarray - The lattice vectors. Lattice vector a1 is lattice_vectors[:, 0], etc. + The lattice vectors. Lattice vector a1 is lattice_vectors[0], etc. unique_positions (:math:`(N_{points}, 3)` :class:`np.ndarray`): The basis positions. @@ -255,9 +255,9 @@ def from_lattice_vectors( @classmethod def from_box_lengths_and_angles( cls, - Lx: float, - Ly: float, - Lz: float, + L1: float, + L2: float, + L3: float, alpha: float, beta: float, gamma: float, @@ -266,9 +266,9 @@ def from_box_lengths_and_angles( """Create a unit cell from box lengths and angles. Args: - Lx (float): The length of the box in the x direction. - Ly (float): The length of the box in the y direction. - Lz (float): The length of the box in the z direction. + L1 (float): The length of the first box vector. + L2 (float): The length of the second box vector. + L3 (float): The length of the third box vector. alpha (float): The angle between the x and y lattice vectors. beta (float): The angle between the x and z lattice vectors. gamma (float): The angle between the y and z lattice vectors. @@ -279,7 +279,7 @@ def from_box_lengths_and_angles( :class:`~.UnitCell`: A unit cell with the given box lengths and angles. """ return cls( - freud.box.Box.from_box_lengths_and_angles(Lx, Ly, Lz, alpha, beta, gamma), + freud.box.Box.from_box_lengths_and_angles(L1, L2, L3, alpha, beta, gamma), unique_positions, ) From 0e78e470247aa0792173e4b73b7beb73fbaacf8e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:37:58 +0000 Subject: [PATCH 13/37] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- freud/box.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freud/box.pyx b/freud/box.pyx index 9febc6353..2c365150d 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -947,7 +947,7 @@ cdef class Box: Args: lattice_vectors (array-like): The lattice vectors. The dimensions of the object should be 3x3. Lattice - vector a1 is lattice_vectors[0], etc. + vector a1 is lattice_vectors[0], etc. dimensions (int): The number of dimensions (Default value = :code:`None`) Returns: From da9d7eab48bbc63893bd16212a074d1fcdc5d417 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Wed, 20 Mar 2024 14:39:50 +0100 Subject: [PATCH 14/37] linter fix --- freud/box.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freud/box.pyx b/freud/box.pyx index 2c365150d..0cdbcf7dd 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -702,7 +702,7 @@ cdef class Box: beta = np.arccos(self.xz/np.sqrt(1+self.xz**2+self.yz**2)) gamma = np.arccos(self.xy/np.sqrt(1+self.xy**2)) L1 = self.Lx - a2 = [self.Ly*self.xy, self.Ly,0] + a2 = [self.Ly*self.xy, self.Ly, 0] a3 = [self.Lz*self.xz, self.Lz*self.yz, self.Lz] L2 = np.linalg.norm(a2) L3 = np.linalg.norm(a3) From aa4354a469bbfdda2729ce3b2903a920f9505b2f Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Wed, 20 Mar 2024 14:50:33 +0100 Subject: [PATCH 15/37] added changelog and credits --- ChangeLog.md | 4 ++++ doc/source/reference/credits.rst | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 11dc46e34..6e11233d1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -8,6 +8,10 @@ and this project adheres to ### Added * New continuous coordination number compute `freud.order.ContinuousCoordination`. +* New class methods for construction of `freud.box.Box` and `freud.data.UnitCell` from + lattice vectors and box lengths and angles. +* New `freud.box.Box.to_box_lengths_and_angles` method for computing box vector lengths + and angles ### Removed * `freud.order.Translational`. diff --git a/doc/source/reference/credits.rst b/doc/source/reference/credits.rst index 46d811605..3ba7af49d 100644 --- a/doc/source/reference/credits.rst +++ b/doc/source/reference/credits.rst @@ -369,6 +369,13 @@ Domagoj Fijan * Contributed code, design, documentation, and testing for ``freud.locality.FilterSANN`` class. * Contributed code, design, documentation, and testing for ``freud.locality.FilterRAD`` class. * Added support for ``gsd.hoomd.Frame`` in ``NeighborQuery.from_system`` calls. +* Added support for ``freud.box.Box`` class methods for construction of boxes and unit + cells from lattice vectors (``freud.box.Box.from_lattice_vectors`` and + ``freud.data.UnitCell.from_lattice_vectors``) and cell lengths and angles + (``freud.box.Box.from_box_lengths_and_angles`` and + ``freud.data.UnitCell.from_box_lengths_and_angles``), as well as a method for + returning cell vector lengths and angles + (``freud.box.Box.to_box_lengths_and_angles``). Andrew Kerr From d5369858486001ac90c1ff7e73b1ff6a848a90cb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:50:53 +0000 Subject: [PATCH 16/37] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ChangeLog.md | 2 +- doc/source/reference/credits.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 6e11233d1..1f5aa99e7 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -11,7 +11,7 @@ and this project adheres to * New class methods for construction of `freud.box.Box` and `freud.data.UnitCell` from lattice vectors and box lengths and angles. * New `freud.box.Box.to_box_lengths_and_angles` method for computing box vector lengths - and angles + and angles ### Removed * `freud.order.Translational`. diff --git a/doc/source/reference/credits.rst b/doc/source/reference/credits.rst index 3ba7af49d..06eeae5b4 100644 --- a/doc/source/reference/credits.rst +++ b/doc/source/reference/credits.rst @@ -374,7 +374,7 @@ Domagoj Fijan ``freud.data.UnitCell.from_lattice_vectors``) and cell lengths and angles (``freud.box.Box.from_box_lengths_and_angles`` and ``freud.data.UnitCell.from_box_lengths_and_angles``), as well as a method for - returning cell vector lengths and angles + returning cell vector lengths and angles (``freud.box.Box.to_box_lengths_and_angles``). Andrew Kerr From 461485ed2ffd68945915911c5ab8b1f13448f160 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 20 Mar 2024 11:30:06 -0400 Subject: [PATCH 17/37] Test dimension detection for `from_lattice_vectors` --- tests/test_box_Box.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_box_Box.py b/tests/test_box_Box.py index a613ffdf1..69a3f8cf2 100644 --- a/tests/test_box_Box.py +++ b/tests/test_box_Box.py @@ -536,9 +536,10 @@ def test_tilted_lattice(self): assert np.isclose(unit_cell.xz, 0.0) assert np.isclose(unit_cell.yz, 0.0) - def test_dimensions(self): + @pytest.mark.parametrize("dim", [2, None]) + def test_dimensions(self, dim): lattice_vectors = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 0]]) - unit_cell = freud.box.Box.from_lattice_vectors(lattice_vectors, dimensions=2) + unit_cell = freud.box.Box.from_lattice_vectors(lattice_vectors, dimensions=dim) assert unit_cell.dimensions == 2 assert np.isclose(unit_cell.Lz, 0) From 90dd30c53f703e4ff005bd2513040f837d54c423 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan <50439291+DomFijan@users.noreply.github.com> Date: Thu, 21 Mar 2024 18:42:18 +0100 Subject: [PATCH 18/37] Update freud/box.pyx Co-authored-by: Tommy Waltmann <53307607+tommy-waltmann@users.noreply.github.com> --- freud/box.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freud/box.pyx b/freud/box.pyx index 0cdbcf7dd..fdfc63a71 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -693,7 +693,7 @@ cdef class Box: r"""Return the box lengths and angles. Returns: - tuple: The box vector lengths and angles in radians + tuple: The box vector lengths and angles in radians :math:`(L_1, L_2, L_3, \alpha, \beta, \gamma)`. """ alpha = np.arccos( (self.xy * self.xz + self.yz) From 8e27b3c001d3332a96acf5fa230ac7d994f48a07 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Thu, 21 Mar 2024 18:42:41 +0100 Subject: [PATCH 19/37] apply requested changes --- freud/box.pyx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/freud/box.pyx b/freud/box.pyx index 0cdbcf7dd..7aa4b394d 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -977,13 +977,13 @@ cdef class Box: @classmethod def from_box_lengths_and_angles( cls, - L1: float, - L2: float, - L3: float, - alpha: float, - beta: float, - gamma: float, - dimensions: int = None, + L1, + L2, + L3, + alpha, + beta, + gamma, + dimensions = None, ): r"""Construct a box from lengths and angles. @@ -991,9 +991,9 @@ cdef class Box: L1 (float): The length of the first lattice vector L2 (float): The length of the second lattice vector L3 (float): The length of the third lattice vector - alpha (float): The angle between the y and z axes in radians - beta (float): The angle between the x and z axes in radians - gamma (float): The angle between the x and y axes in radians + alpha (float): The angle between second and third lattice vector in radians + beta (float): The angle between first and third lattice vector in radians + gamma (float): The angle between the first and second lattice vector in radians dimensions (int): The number of dimensions (Default value = :code:`None`) Returns: From 5bd2c0813d6390e36c4622e513aadec2cceb1aad Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Thu, 21 Mar 2024 19:01:06 +0100 Subject: [PATCH 20/37] revert unwanted changes and delete tests --- freud/box.pyx | 45 +++----------------------------------- freud/data.py | 50 ------------------------------------------- tests/test_box_Box.py | 35 ------------------------------ 3 files changed, 3 insertions(+), 127 deletions(-) diff --git a/freud/box.pyx b/freud/box.pyx index c18a6d9c3..3f2947489 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -693,7 +693,8 @@ cdef class Box: r"""Return the box lengths and angles. Returns: - tuple: The box vector lengths and angles in radians :math:`(L_1, L_2, L_3, \alpha, \beta, \gamma)`. + tuple: The box vector lengths and angles in radians + :math:`(L_1, L_2, L_3, \alpha, \beta, \gamma)`. """ alpha = np.arccos( (self.xy * self.xz + self.yz) @@ -940,50 +941,10 @@ cdef class Box: "positional argument: L") return cls(Lx=L, Ly=L, Lz=0, xy=0, xz=0, yz=0, is2D=True) - @classmethod - def from_lattice_vectors(cls, lattice_vectors, dimensions: int = None): - """Create a unit cell from lattice vectors. - - Args: - lattice_vectors (array-like): - The lattice vectors. The dimensions of the object should be 3x3. Lattice - vector a1 is lattice_vectors[0], etc. - dimensions (int): The number of dimensions (Default value = :code:`None`) - - Returns: - :class:`~.UnitCell`: A unit cell with the given lattice vectors. - """ - lattice_matrix = np.asarray(lattice_vectors, dtype=np.float32) - v0 = lattice_matrix[0] - v1 = lattice_matrix[1] - v2 = lattice_matrix[2] - Lx = np.sqrt(np.dot(v0, v0)) - a2x = np.dot(v0, v1) / Lx - Ly = np.sqrt(np.dot(v1, v1) - a2x * a2x) - xy = a2x / Ly - v0xv1 = np.cross(v0, v1) - v0xv1mag = np.sqrt(np.dot(v0xv1, v0xv1)) - Lz = np.dot(v2, v0xv1) / v0xv1mag - if Lz != 0: - a3x = np.dot(v0, v2) / Lx - xz = a3x / Lz - yz = (np.dot(v1, v2) - a2x * a3x) / (Ly * Lz) - else: - xz = yz = 0 - if dimensions is None: - dimensions = 2 if Lz == 0 else 3 - return cls.from_box([Lx, Ly, Lz, xy, xz, yz], dimensions=dimensions) @classmethod def from_box_lengths_and_angles( - cls, - L1, - L2, - L3, - alpha, - beta, - gamma, - dimensions = None, + cls, L1, L2, L3, alpha, beta, gamma, dimensions = None, ): r"""Construct a box from lengths and angles. diff --git a/freud/data.py b/freud/data.py index aa5261c2a..87c0c0bfb 100644 --- a/freud/data.py +++ b/freud/data.py @@ -233,56 +233,6 @@ def hex(cls): fractions = np.array([[0, 0, 0], [0.5, 0.5, 0]]) return cls([1, np.sqrt(3)], fractions) - @classmethod - def from_lattice_vectors( - cls, lattice_vectors: np.ndarray, unique_positions: np.ndarray - ): - """Create a unit cell from lattice vectors. - - Args: - lattice_vectors (:math:`(3, 3)` :class:`np.ndarray - The lattice vectors. Lattice vector a1 is lattice_vectors[0], etc. - unique_positions (:math:`(N_{points}, 3)` :class:`np.ndarray`): - The basis positions. - - Returns: - :class:`~.UnitCell`: A unit cell with the given lattice vectors. - """ - return cls( - freud.box.Box.from_lattice_vectors(lattice_vectors), unique_positions - ) - - @classmethod - def from_box_lengths_and_angles( - cls, - L1: float, - L2: float, - L3: float, - alpha: float, - beta: float, - gamma: float, - unique_positions: np.ndarray, - ): - """Create a unit cell from box lengths and angles. - - Args: - L1 (float): The length of the first box vector. - L2 (float): The length of the second box vector. - L3 (float): The length of the third box vector. - alpha (float): The angle between the x and y lattice vectors. - beta (float): The angle between the x and z lattice vectors. - gamma (float): The angle between the y and z lattice vectors. - unique_positions (:math:`(N_{points}, 3)` :class:`np.ndarray - The basis positions. - - Returns: - :class:`~.UnitCell`: A unit cell with the given box lengths and angles. - """ - return cls( - freud.box.Box.from_box_lengths_and_angles(L1, L2, L3, alpha, beta, gamma), - unique_positions, - ) - def make_random_system(box_size, num_points, is2D=False, seed=None): r"""Helper function to make random points with a cubic or square box. diff --git a/tests/test_box_Box.py b/tests/test_box_Box.py index 69a3f8cf2..fca40e6b3 100644 --- a/tests/test_box_Box.py +++ b/tests/test_box_Box.py @@ -507,41 +507,6 @@ def test_to_and_from_box_lengths_and_angles(self): lengths_and_angles_computed = box.to_box_lengths_and_angles() assert np.allclose(lengths_and_angles_computed, original_box_lengths_and_angles) - def test_from_lattice_vectors_cubic_lattice(self): - lattice_vectors = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) - unit_cell = freud.box.Box.from_lattice_vectors(lattice_vectors) - assert unit_cell.Lx == 1 - assert unit_cell.Ly == 1 - assert unit_cell.Lz == 1 - assert unit_cell.xy == 0 - assert unit_cell.xz == 0 - assert unit_cell.yz == 0 - - def test_from_lattice_vectors_non_cubic_lattice(self): - box = freud.box.Box.from_lattice_vectors([[1, 0, 0], [0, 2, 0], [0, 0, 3]]) - assert box.Lx == 1 - assert box.Ly == 2 - assert box.Lz == 3 - assert box.xy == 0 - assert box.xz == 0 - assert box.yz == 0 - - def test_tilted_lattice(self): - lattice_vectors = np.array([[1, 0, 0], [0.5, np.sqrt(3) / 2, 0], [0, 0, 1]]) - unit_cell = freud.box.Box.from_lattice_vectors(lattice_vectors) - assert np.isclose(unit_cell.Lx, 1.0) - assert np.isclose(unit_cell.Ly, np.sqrt(3) / 2) - assert np.isclose(unit_cell.Lz, 1.0) - assert np.isclose(unit_cell.xy, np.sqrt(3) / 3) - assert np.isclose(unit_cell.xz, 0.0) - assert np.isclose(unit_cell.yz, 0.0) - - @pytest.mark.parametrize("dim", [2, None]) - def test_dimensions(self, dim): - lattice_vectors = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 0]]) - unit_cell = freud.box.Box.from_lattice_vectors(lattice_vectors, dimensions=dim) - assert unit_cell.dimensions == 2 - assert np.isclose(unit_cell.Lz, 0) def test_matrix(self): box = freud.box.Box(2, 2, 2, 1, 0.5, 0.1) From 6a9b7408a6890509b305de9ecb8810d3be3ca4a9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 18:01:29 +0000 Subject: [PATCH 21/37] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_box_Box.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_box_Box.py b/tests/test_box_Box.py index fca40e6b3..dbb881f7b 100644 --- a/tests/test_box_Box.py +++ b/tests/test_box_Box.py @@ -507,7 +507,6 @@ def test_to_and_from_box_lengths_and_angles(self): lengths_and_angles_computed = box.to_box_lengths_and_angles() assert np.allclose(lengths_and_angles_computed, original_box_lengths_and_angles) - def test_matrix(self): box = freud.box.Box(2, 2, 2, 1, 0.5, 0.1) box2 = freud.box.Box.from_matrix(box.to_matrix()) From 1a45db106d9397cfb45e34134a14782bfe46720e Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Thu, 21 Mar 2024 19:03:19 +0100 Subject: [PATCH 22/37] fix format --- freud/box.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/freud/box.pyx b/freud/box.pyx index 3f2947489..e381a94b5 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -941,10 +941,9 @@ cdef class Box: "positional argument: L") return cls(Lx=L, Ly=L, Lz=0, xy=0, xz=0, yz=0, is2D=True) - @classmethod def from_box_lengths_and_angles( - cls, L1, L2, L3, alpha, beta, gamma, dimensions = None, + cls, L1, L2, L3, alpha, beta, gamma, dimensions=None, ): r"""Construct a box from lengths and angles. From ace7fac122df817f205f543f2dd30662ff8cd463 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Thu, 21 Mar 2024 19:06:57 +0100 Subject: [PATCH 23/37] final format fix --- freud/box.pyx | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/freud/box.pyx b/freud/box.pyx index e381a94b5..6b0e44fdf 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -693,8 +693,9 @@ cdef class Box: r"""Return the box lengths and angles. Returns: - tuple: The box vector lengths and angles in radians - :math:`(L_1, L_2, L_3, \alpha, \beta, \gamma)`. + tuple: + The box vector lengths and angles in radians + :math:`(L_1, L_2, L_3, \alpha, \beta, \gamma)`. """ alpha = np.arccos( (self.xy * self.xz + self.yz) @@ -948,13 +949,20 @@ cdef class Box: r"""Construct a box from lengths and angles. Args: - L1 (float): The length of the first lattice vector - L2 (float): The length of the second lattice vector - L3 (float): The length of the third lattice vector - alpha (float): The angle between second and third lattice vector in radians - beta (float): The angle between first and third lattice vector in radians - gamma (float): The angle between the first and second lattice vector in radians - dimensions (int): The number of dimensions (Default value = :code:`None`) + L1 (float): + The length of the first lattice vector + L2 (float): + The length of the second lattice vector + L3 (float): + The length of the third lattice vector + alpha (float): + The angle between second and third lattice vector in radians + beta (float): + The angle between first and third lattice vector in radians + gamma (float): + The angle between the first and second lattice vector in radians + dimensions (int): + The number of dimensions (Default value = :code:`None`) Returns: :class:`freud.box.Box`: The resulting box object. From 6f6932e5ee411286cda52104cd30305c734a22f3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 18:08:03 +0000 Subject: [PATCH 24/37] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- freud/box.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freud/box.pyx b/freud/box.pyx index 6b0e44fdf..aed409b24 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -693,7 +693,7 @@ cdef class Box: r"""Return the box lengths and angles. Returns: - tuple: + tuple: The box vector lengths and angles in radians :math:`(L_1, L_2, L_3, \alpha, \beta, \gamma)`. """ @@ -957,9 +957,9 @@ cdef class Box: The length of the third lattice vector alpha (float): The angle between second and third lattice vector in radians - beta (float): + beta (float): The angle between first and third lattice vector in radians - gamma (float): + gamma (float): The angle between the first and second lattice vector in radians dimensions (int): The number of dimensions (Default value = :code:`None`) From ae9683628a2797afd6a85b160cdd7ea3fe74c604 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Thu, 21 Mar 2024 19:52:26 +0100 Subject: [PATCH 25/37] fix from_box_lengths_and_angles --- freud/box.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freud/box.pyx b/freud/box.pyx index aed409b24..0450b4a85 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -975,7 +975,7 @@ cdef class Box: a3 = np.array([L3 * a3x, L3 * a3y, L3 * a3z]) if dimensions is None: dimensions = 2 if L3 == 0 else 3 - return cls.from_lattice_vectors(np.array([a1, a2, a3]), dimensions=dimensions) + return cls.from_matrix(np.array([a1, a2, a3]).T, dimensions=dimensions) cdef BoxFromCPP(const freud._box.Box & cppbox): From 3aab7cb3ca64314321b98048b0cfe7c3db0fe239 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Thu, 21 Mar 2024 19:56:20 +0100 Subject: [PATCH 26/37] fix changelog and credits --- ChangeLog.md | 5 ++--- doc/source/reference/credits.rst | 9 +++------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 1f5aa99e7..21d87cb66 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -8,10 +8,9 @@ and this project adheres to ### Added * New continuous coordination number compute `freud.order.ContinuousCoordination`. -* New class methods for construction of `freud.box.Box` and `freud.data.UnitCell` from - lattice vectors and box lengths and angles. +* New class methods for construction of `freud.box.Box` from box lengths and angles. * New `freud.box.Box.to_box_lengths_and_angles` method for computing box vector lengths - and angles + and angles. ### Removed * `freud.order.Translational`. diff --git a/doc/source/reference/credits.rst b/doc/source/reference/credits.rst index 06eeae5b4..415f88fa8 100644 --- a/doc/source/reference/credits.rst +++ b/doc/source/reference/credits.rst @@ -369,12 +369,9 @@ Domagoj Fijan * Contributed code, design, documentation, and testing for ``freud.locality.FilterSANN`` class. * Contributed code, design, documentation, and testing for ``freud.locality.FilterRAD`` class. * Added support for ``gsd.hoomd.Frame`` in ``NeighborQuery.from_system`` calls. -* Added support for ``freud.box.Box`` class methods for construction of boxes and unit - cells from lattice vectors (``freud.box.Box.from_lattice_vectors`` and - ``freud.data.UnitCell.from_lattice_vectors``) and cell lengths and angles - (``freud.box.Box.from_box_lengths_and_angles`` and - ``freud.data.UnitCell.from_box_lengths_and_angles``), as well as a method for - returning cell vector lengths and angles +* Added support for ``freud.box.Box`` class methods for construction of boxes from cell + lengths and angles (``freud.box.Box.from_box_lengths_and_angles``), as well as a + method for returning box vector lengths and angles (``freud.box.Box.to_box_lengths_and_angles``). Andrew Kerr From a7c18e2ae8574c34f96f6adc4d587a92ec176eb6 Mon Sep 17 00:00:00 2001 From: Tommy Waltmann <53307607+tommy-waltmann@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:37:56 -0400 Subject: [PATCH 27/37] Update freud/box.pyx --- freud/box.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freud/box.pyx b/freud/box.pyx index 0450b4a85..dcbd70c40 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -962,7 +962,7 @@ cdef class Box: gamma (float): The angle between the first and second lattice vector in radians dimensions (int): - The number of dimensions (Default value = :code:`None`) + The number of dimensions (Default value = :code:`None`). Returns: :class:`freud.box.Box`: The resulting box object. From c26e88e38662e912d94727e30b9c6a9e5f53c384 Mon Sep 17 00:00:00 2001 From: Tommy Waltmann <53307607+tommy-waltmann@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:38:43 -0400 Subject: [PATCH 28/37] Apply suggestions from code review --- freud/box.pyx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/freud/box.pyx b/freud/box.pyx index dcbd70c40..c0d10613d 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -950,17 +950,17 @@ cdef class Box: Args: L1 (float): - The length of the first lattice vector + The length of the first lattice vector. L2 (float): - The length of the second lattice vector + The length of the second lattice vector. L3 (float): - The length of the third lattice vector + The length of the third lattice vector. alpha (float): - The angle between second and third lattice vector in radians + The angle between second and third lattice vector in radians. beta (float): - The angle between first and third lattice vector in radians + The angle between first and third lattice vector in radians. gamma (float): - The angle between the first and second lattice vector in radians + The angle between the first and second lattice vector in radians. dimensions (int): The number of dimensions (Default value = :code:`None`). From ae56eff6e150548aa72b54e1d15b913189275f46 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan <50439291+DomFijan@users.noreply.github.com> Date: Fri, 22 Mar 2024 10:56:49 +0100 Subject: [PATCH 29/37] Update ChangeLog.md Co-authored-by: Tommy Waltmann <53307607+tommy-waltmann@users.noreply.github.com> --- ChangeLog.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 21d87cb66..5b27e681b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -8,9 +8,7 @@ and this project adheres to ### Added * New continuous coordination number compute `freud.order.ContinuousCoordination`. -* New class methods for construction of `freud.box.Box` from box lengths and angles. -* New `freud.box.Box.to_box_lengths_and_angles` method for computing box vector lengths - and angles. +* New methods for conversion of box lengths and angles to/from `freud.box.Box`. ### Removed * `freud.order.Translational`. From a3daf1b58016dbdc7fe83499951540bfa2573e96 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Fri, 22 Mar 2024 11:29:32 +0100 Subject: [PATCH 30/37] update docs to state range of angles allowed --- freud/box.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freud/box.pyx b/freud/box.pyx index c0d10613d..eb1030d45 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -948,6 +948,8 @@ cdef class Box: ): r"""Construct a box from lengths and angles. + All the angles provided must be between 0 and :math:`\pi`. + Args: L1 (float): The length of the first lattice vector. From dffb7725f306366a4a1762113a370e56d6bb4836 Mon Sep 17 00:00:00 2001 From: Tommy Waltmann Date: Fri, 22 Mar 2024 09:56:06 -0400 Subject: [PATCH 31/37] update test --- tests/test_box_Box.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/test_box_Box.py b/tests/test_box_Box.py index dbb881f7b..7b19909ef 100644 --- a/tests/test_box_Box.py +++ b/tests/test_box_Box.py @@ -494,18 +494,19 @@ def test_standard_orthogonal_box(self): def test_to_and_from_box_lengths_and_angles(self): original_box_lengths_and_angles = ( - 1, - 2, - 3, - np.pi / 3, - np.pi / 2.25, - np.pi / 2.35, + np.random.uniform(0, 100000), + np.random.uniform(0, 100000), + np.random.uniform(0, 100000), + np.random.uniform(0, np.pi), + np.random.uniform(0, np.pi), + np.random.uniform(0, np.pi), ) box = freud.box.Box.from_box_lengths_and_angles( *original_box_lengths_and_angles ) lengths_and_angles_computed = box.to_box_lengths_and_angles() - assert np.allclose(lengths_and_angles_computed, original_box_lengths_and_angles) + np.testing.assert_allclose(lengths_and_angles_computed, + original_box_lengths_and_angles, rtol=1e-6) def test_matrix(self): box = freud.box.Box(2, 2, 2, 1, 0.5, 0.1) From a3548a12a82a666e42c80d4f9dfa6acbde7f7c76 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 22 Mar 2024 13:56:31 +0000 Subject: [PATCH 32/37] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_box_Box.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_box_Box.py b/tests/test_box_Box.py index 7b19909ef..3fba3f423 100644 --- a/tests/test_box_Box.py +++ b/tests/test_box_Box.py @@ -505,8 +505,9 @@ def test_to_and_from_box_lengths_and_angles(self): *original_box_lengths_and_angles ) lengths_and_angles_computed = box.to_box_lengths_and_angles() - np.testing.assert_allclose(lengths_and_angles_computed, - original_box_lengths_and_angles, rtol=1e-6) + np.testing.assert_allclose( + lengths_and_angles_computed, original_box_lengths_and_angles, rtol=1e-6 + ) def test_matrix(self): box = freud.box.Box(2, 2, 2, 1, 0.5, 0.1) From a3449d8f99e0c86a44c94412be0fc9d8205a3ecd Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 26 Mar 2024 14:01:00 -0400 Subject: [PATCH 33/37] Set nonzero atol for assert_allclose in test_box_Box.py --- tests/test_box_Box.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_box_Box.py b/tests/test_box_Box.py index 3fba3f423..f6f583e88 100644 --- a/tests/test_box_Box.py +++ b/tests/test_box_Box.py @@ -506,7 +506,10 @@ def test_to_and_from_box_lengths_and_angles(self): ) lengths_and_angles_computed = box.to_box_lengths_and_angles() np.testing.assert_allclose( - lengths_and_angles_computed, original_box_lengths_and_angles, rtol=1e-6 + lengths_and_angles_computed, + original_box_lengths_and_angles, + rtol=1e-6, + atol=1e-14, ) def test_matrix(self): From ca710c330b345458bc8b548bae143e432b7e6930 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Thu, 11 Apr 2024 18:03:05 -0400 Subject: [PATCH 34/37] update from_box_lengths_and_angles docstring to contain restrictions for angles and raise valueerror when violated --- freud/box.pyx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/freud/box.pyx b/freud/box.pyx index eb1030d45..82fe5d6db 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -946,7 +946,7 @@ cdef class Box: def from_box_lengths_and_angles( cls, L1, L2, L3, alpha, beta, gamma, dimensions=None, ): - r"""Construct a box from lengths and angles. + r"""Construct a box from lengths and angles (in radians). All the angles provided must be between 0 and :math:`\pi`. @@ -958,17 +958,26 @@ cdef class Box: L3 (float): The length of the third lattice vector. alpha (float): - The angle between second and third lattice vector in radians. + The angle between second and third lattice vector in radians (must be + between 0 and :math:`\pi`). beta (float): - The angle between first and third lattice vector in radians. + The angle between first and third lattice vector in radians (must be + between 0 and :math:`\pi`). gamma (float): - The angle between the first and second lattice vector in radians. + The angle between the first and second lattice vector in radians (must + be between 0 and :math:`\pi`). dimensions (int): The number of dimensions (Default value = :code:`None`). Returns: :class:`freud.box.Box`: The resulting box object. """ + if not 0 < alpha < np.pi: + raise ValueError("alpha must be between 0 and pi.") + if not 0 < beta < np.pi: + raise ValueError("beta must be between 0 and pi.") + if not 0 < gamma < np.pi: + raise ValueError("gamma must be between 0 and pi.") a1 = np.array([L1, 0, 0]) a2 = np.array([L2 * np.cos(gamma), L2 * np.sin(gamma), 0]) a3x = np.cos(beta) From ad8d8b3b692c01ea6ae5a050683cd13030f526ac Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Thu, 11 Apr 2024 19:16:17 -0400 Subject: [PATCH 35/37] raise error if box has negative volume --- freud/box.pyx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/freud/box.pyx b/freud/box.pyx index 82fe5d6db..1b12fbcae 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -982,7 +982,10 @@ cdef class Box: a2 = np.array([L2 * np.cos(gamma), L2 * np.sin(gamma), 0]) a3x = np.cos(beta) a3y = (np.cos(alpha) - np.cos(beta) * np.cos(gamma)) / np.sin(gamma) - a3z = np.sqrt(1 - a3x**2 - a3y**2) + under_sqrt = 1 - a3x**2 - a3y**2 + if under_sqrt < 0: + raise ValueError("The provided angles can not form a valid box.") + a3z = np.sqrt() a3 = np.array([L3 * a3x, L3 * a3y, L3 * a3z]) if dimensions is None: dimensions = 2 if L3 == 0 else 3 From b51d8d74df90a063cdd51cf609d613af599d47a3 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Thu, 11 Apr 2024 19:16:36 -0400 Subject: [PATCH 36/37] fix sqrt --- freud/box.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freud/box.pyx b/freud/box.pyx index 1b12fbcae..f72151bbf 100644 --- a/freud/box.pyx +++ b/freud/box.pyx @@ -985,7 +985,7 @@ cdef class Box: under_sqrt = 1 - a3x**2 - a3y**2 if under_sqrt < 0: raise ValueError("The provided angles can not form a valid box.") - a3z = np.sqrt() + a3z = np.sqrt(under_sqrt) a3 = np.array([L3 * a3x, L3 * a3y, L3 * a3z]) if dimensions is None: dimensions = 2 if L3 == 0 else 3 From d78e08e9b7c273910432f1b8bd734ef8752fd920 Mon Sep 17 00:00:00 2001 From: Domagoj Fijan Date: Tue, 23 Apr 2024 12:00:55 -0400 Subject: [PATCH 37/37] fix test to check for imposible boxes --- tests/test_box_Box.py | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/tests/test_box_Box.py b/tests/test_box_Box.py index f6f583e88..c810925b9 100644 --- a/tests/test_box_Box.py +++ b/tests/test_box_Box.py @@ -501,16 +501,35 @@ def test_to_and_from_box_lengths_and_angles(self): np.random.uniform(0, np.pi), np.random.uniform(0, np.pi), ) - box = freud.box.Box.from_box_lengths_and_angles( - *original_box_lengths_and_angles - ) - lengths_and_angles_computed = box.to_box_lengths_and_angles() - np.testing.assert_allclose( - lengths_and_angles_computed, - original_box_lengths_and_angles, - rtol=1e-6, - atol=1e-14, - ) + if ( + 1 + - np.cos(original_box_lengths_and_angles[4]) ** 2 + - ( + ( + np.cos(original_box_lengths_and_angles[3]) + - np.cos(original_box_lengths_and_angles[4]) + * np.cos(original_box_lengths_and_angles[5]) + ) + / np.sin(original_box_lengths_and_angles[5]) + ) + ** 2 + < 0 + ): + with pytest.raises(ValueError): + freud.box.Box.from_box_lengths_and_angles( + *original_box_lengths_and_angles + ) + else: + box = freud.box.Box.from_box_lengths_and_angles( + *original_box_lengths_and_angles + ) + lengths_and_angles_computed = box.to_box_lengths_and_angles() + np.testing.assert_allclose( + lengths_and_angles_computed, + original_box_lengths_and_angles, + rtol=1e-6, + atol=1e-14, + ) def test_matrix(self): box = freud.box.Box(2, 2, 2, 1, 0.5, 0.1)