From 77aee1e9ffb8d1cbb682af644d7da0eb68590b9c Mon Sep 17 00:00:00 2001 From: samwaseda Date: Tue, 8 Aug 2023 09:41:47 +0000 Subject: [PATCH 01/12] Show box cell --- structuretoolkit/visualize.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/structuretoolkit/visualize.py b/structuretoolkit/visualize.py index ae688c441..8b33f1adb 100644 --- a/structuretoolkit/visualize.py +++ b/structuretoolkit/visualize.py @@ -120,6 +120,7 @@ def plot3d( elif mode == "plotly": return _plot3d_plotly( structure=structure, + show_cell=show_cell, camera=camera, particle_size=particle_size, select_atoms=select_atoms, @@ -143,8 +144,18 @@ def plot3d( raise ValueError("plot method not recognized") +def _get_frame(cell): + vertices = np.array([[0, 0, 0], [0, 0, 1], [0, 1, 1], [0, 1, 0], [0, 0, 0]]) + return [ + f(vertices + v) @ cell + for f in [lambda x: x, lambda x: np.roll(x, 1, axis=-1)] + for v in [0, [1, 0, 0]] + ] + + def _plot3d_plotly( structure, + show_cell=False, scalar_field=None, select_atoms=None, particle_size=1.0, @@ -177,6 +188,7 @@ def _plot3d_plotly( """ try: import plotly.express as px + import plotly.graph_objects as go except ModuleNotFoundError: raise ModuleNotFoundError("plotly not installed - use plot3d instead") if select_atoms is None: @@ -196,6 +208,13 @@ def _plot3d_plotly( scale=particle_size / (0.1 * structure.get_volume() ** (1 / 3)), ), ) + if show_cell: + data = fig.data + for lines in _get_frame(structure.cell): + fig = px.line_3d(**{xx: vv for xx, vv in zip(['x', 'y', 'z'], lines.T)}) + fig.update_traces(line_color="#000000") + data = fig.data + data + fig = go.Figure(data=data) fig.layout.scene.camera.projection.type = camera rot = _get_orientation(view_plane).T rot[0, :] *= distance_from_camera * 1.25 From 90af2b3536c2bb893d4c3023ddbd46a40edbd544 Mon Sep 17 00:00:00 2001 From: samwaseda Date: Fri, 12 Jan 2024 09:21:16 +0000 Subject: [PATCH 02/12] make _get_frame easier to follow? --- structuretoolkit/visualize.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/structuretoolkit/visualize.py b/structuretoolkit/visualize.py index 8b33f1adb..5555bc6b0 100644 --- a/structuretoolkit/visualize.py +++ b/structuretoolkit/visualize.py @@ -145,12 +145,11 @@ def plot3d( def _get_frame(cell): - vertices = np.array([[0, 0, 0], [0, 0, 1], [0, 1, 1], [0, 1, 0], [0, 0, 0]]) - return [ - f(vertices + v) @ cell - for f in [lambda x: x, lambda x: np.roll(x, 1, axis=-1)] - for v in [0, [1, 0, 0]] - ] + lines_dz = np.stack(np.meshgrid(*3 * [[0, 1]], indexing="ij"), axis=-1) + all_lines = np.reshape( + [np.roll(lines_dz, i, axis=-1) for i in range(3)], (-1, 2, 3) + ) + return all_lines @ cell def _plot3d_plotly( From 1f95b7df5c92240366160185e27d2486631bd397 Mon Sep 17 00:00:00 2001 From: samwaseda Date: Fri, 12 Jan 2024 09:23:29 +0000 Subject: [PATCH 03/12] change default of show_cell --- structuretoolkit/visualize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structuretoolkit/visualize.py b/structuretoolkit/visualize.py index 5555bc6b0..3ffea560f 100644 --- a/structuretoolkit/visualize.py +++ b/structuretoolkit/visualize.py @@ -154,7 +154,7 @@ def _get_frame(cell): def _plot3d_plotly( structure, - show_cell=False, + show_cell=True, scalar_field=None, select_atoms=None, particle_size=1.0, From 357303b3ff240e8a452c940dbcfc5ce968c937ca Mon Sep 17 00:00:00 2001 From: samwaseda Date: Fri, 12 Jan 2024 09:46:11 +0000 Subject: [PATCH 04/12] add tests for get_frame --- tests/test_visualize.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/test_visualize.py b/tests/test_visualize.py index 5abc2ae58..e621ab2d9 100644 --- a/tests/test_visualize.py +++ b/tests/test_visualize.py @@ -4,7 +4,7 @@ import unittest import numpy as np -from structuretoolkit.visualize import _get_flattened_orientation +from structuretoolkit.visualize import _get_flattened_orientation, _get_frame class TestAtoms(unittest.TestCase): @@ -21,6 +21,19 @@ def test_get_flattened_orientation(self): R = np.array(_get_flattened_orientation(R, 1)).reshape(4, 4) self.assertAlmostEqual(np.linalg.det(R), 1) + def test_get_frame(self): + frame = _get_frame(np.eye(3)) + self.assertLessEqual( + np.unique(frame.reshape(-1, 6), axis=0, return_counts=True)[1].max(), + 1 + ) + dx, counts = np.unique( + np.diff(frame, axis=-2).squeeze().astype(int), axis=0, return_counts=True + ) + self.assertEqual(dx.ptp(), 1) + self.assertEqual(counts.min(), 4) + self.assertEqual(counts.max(), 4) + if __name__ == "__main__": unittest.main() From 47a65f6c41c285fe4a9238b70ddba54ffbdf25bd Mon Sep 17 00:00:00 2001 From: samwaseda Date: Fri, 12 Jan 2024 09:51:06 +0000 Subject: [PATCH 05/12] satisfy Black --- structuretoolkit/visualize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structuretoolkit/visualize.py b/structuretoolkit/visualize.py index 3ffea560f..aa67049fe 100644 --- a/structuretoolkit/visualize.py +++ b/structuretoolkit/visualize.py @@ -210,7 +210,7 @@ def _plot3d_plotly( if show_cell: data = fig.data for lines in _get_frame(structure.cell): - fig = px.line_3d(**{xx: vv for xx, vv in zip(['x', 'y', 'z'], lines.T)}) + fig = px.line_3d(**{xx: vv for xx, vv in zip(["x", "y", "z"], lines.T)}) fig.update_traces(line_color="#000000") data = fig.data + data fig = go.Figure(data=data) From 1125da76ad1922d2ca157e376535544e8d46e4af Mon Sep 17 00:00:00 2001 From: samwaseda Date: Thu, 8 Feb 2024 14:21:06 +0000 Subject: [PATCH 06/12] rename method --- structuretoolkit/visualize.py | 4 ++-- tests/test_visualize.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/structuretoolkit/visualize.py b/structuretoolkit/visualize.py index aa67049fe..bff18b03a 100644 --- a/structuretoolkit/visualize.py +++ b/structuretoolkit/visualize.py @@ -144,7 +144,7 @@ def plot3d( raise ValueError("plot method not recognized") -def _get_frame(cell): +def _get_box_skeleton(cell): lines_dz = np.stack(np.meshgrid(*3 * [[0, 1]], indexing="ij"), axis=-1) all_lines = np.reshape( [np.roll(lines_dz, i, axis=-1) for i in range(3)], (-1, 2, 3) @@ -209,7 +209,7 @@ def _plot3d_plotly( ) if show_cell: data = fig.data - for lines in _get_frame(structure.cell): + for lines in _get_box_sleketon(structure.cell): fig = px.line_3d(**{xx: vv for xx, vv in zip(["x", "y", "z"], lines.T)}) fig.update_traces(line_color="#000000") data = fig.data + data diff --git a/tests/test_visualize.py b/tests/test_visualize.py index e621ab2d9..4a6387978 100644 --- a/tests/test_visualize.py +++ b/tests/test_visualize.py @@ -4,7 +4,7 @@ import unittest import numpy as np -from structuretoolkit.visualize import _get_flattened_orientation, _get_frame +from structuretoolkit.visualize import _get_flattened_orientation, _get_box_skeleton class TestAtoms(unittest.TestCase): @@ -22,7 +22,7 @@ def test_get_flattened_orientation(self): self.assertAlmostEqual(np.linalg.det(R), 1) def test_get_frame(self): - frame = _get_frame(np.eye(3)) + frame = _get_box_skeleton(np.eye(3)) self.assertLessEqual( np.unique(frame.reshape(-1, 6), axis=0, return_counts=True)[1].max(), 1 From a2fab2ecdfc9f824734c3a639942600c362e100a Mon Sep 17 00:00:00 2001 From: Sam Dareska <37879103+samwaseda@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:22:23 +0100 Subject: [PATCH 07/12] Update structuretoolkit/visualize.py Co-authored-by: Liam Huber --- structuretoolkit/visualize.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/structuretoolkit/visualize.py b/structuretoolkit/visualize.py index bff18b03a..d5c6c52a3 100644 --- a/structuretoolkit/visualize.py +++ b/structuretoolkit/visualize.py @@ -146,9 +146,12 @@ def plot3d( def _get_box_skeleton(cell): lines_dz = np.stack(np.meshgrid(*3 * [[0, 1]], indexing="ij"), axis=-1) + # eight corners of a unit cube, paired as four z-axis lines + all_lines = np.reshape( [np.roll(lines_dz, i, axis=-1) for i in range(3)], (-1, 2, 3) ) + # All 12 two-point lines on the unit square return all_lines @ cell From 2786aaf19ef914e1c51849696ef7603509c7f70a Mon Sep 17 00:00:00 2001 From: pyiron-runner Date: Thu, 8 Feb 2024 14:40:30 +0000 Subject: [PATCH 08/12] Format black --- structuretoolkit/analyse/spatial.py | 6 +++--- structuretoolkit/analyse/strain.py | 1 - structuretoolkit/analyse/symmetry.py | 1 - structuretoolkit/visualize.py | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/structuretoolkit/analyse/spatial.py b/structuretoolkit/analyse/spatial.py index 5b4fc988f..252819a1f 100644 --- a/structuretoolkit/analyse/spatial.py +++ b/structuretoolkit/analyse/spatial.py @@ -88,9 +88,9 @@ def set_to_high_symmetry_points(positions, structure, neigh, decimals=4): def cluster_by_steinhardt(positions, neigh, l_values, q_eps, var_ratio, min_samples): """ Clusters candidate positions via Steinhardt parameters and the variance in distances to host atoms. - + The cluster that has the lowest variance is returned, i.e. those positions that have the most "regular" coordination polyhedra. - + Args: positions (array): candidate positions neigh (Neighbor): neighborhood information of the candidate positions @@ -98,7 +98,7 @@ def cluster_by_steinhardt(positions, neigh, l_values, q_eps, var_ratio, min_samp q_eps (float): maximum intercluster distance in steinhardt parameters for DBSCAN clustering var_ratio (float): multiplier to make steinhardt's and distance variance numerically comparable min_samples (int): minimum size of clusters - + Returns: array: Positions of the most likely interstitial sites """ diff --git a/structuretoolkit/analyse/strain.py b/structuretoolkit/analyse/strain.py index 3632c9dda..f9ebb6aaa 100644 --- a/structuretoolkit/analyse/strain.py +++ b/structuretoolkit/analyse/strain.py @@ -6,7 +6,6 @@ class Strain: - """ Calculate local strain of each atom following the Lagrangian strain tensor: diff --git a/structuretoolkit/analyse/symmetry.py b/structuretoolkit/analyse/symmetry.py index 9fd72b1d2..4bb4e6f4d 100644 --- a/structuretoolkit/analyse/symmetry.py +++ b/structuretoolkit/analyse/symmetry.py @@ -24,7 +24,6 @@ class Symmetry(dict): - """ Return a class for operations related to box symmetries. Main attributes: diff --git a/structuretoolkit/visualize.py b/structuretoolkit/visualize.py index d5c6c52a3..2fa2a7a5b 100644 --- a/structuretoolkit/visualize.py +++ b/structuretoolkit/visualize.py @@ -147,7 +147,7 @@ def plot3d( def _get_box_skeleton(cell): lines_dz = np.stack(np.meshgrid(*3 * [[0, 1]], indexing="ij"), axis=-1) # eight corners of a unit cube, paired as four z-axis lines - + all_lines = np.reshape( [np.roll(lines_dz, i, axis=-1) for i in range(3)], (-1, 2, 3) ) From 79291b6a3969099c57ce74f85f5d5c18ecf4b6bf Mon Sep 17 00:00:00 2001 From: samwaseda Date: Thu, 8 Feb 2024 18:26:53 +0000 Subject: [PATCH 09/12] I don't know how to write in English --- structuretoolkit/visualize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structuretoolkit/visualize.py b/structuretoolkit/visualize.py index bff18b03a..d2062af8f 100644 --- a/structuretoolkit/visualize.py +++ b/structuretoolkit/visualize.py @@ -209,7 +209,7 @@ def _plot3d_plotly( ) if show_cell: data = fig.data - for lines in _get_box_sleketon(structure.cell): + for lines in _get_box_skeleton(structure.cell): fig = px.line_3d(**{xx: vv for xx, vv in zip(["x", "y", "z"], lines.T)}) fig.update_traces(line_color="#000000") data = fig.data + data From 5a98833828d9a40bbcdf6a66fded76aedb6e974e Mon Sep 17 00:00:00 2001 From: samwaseda Date: Thu, 8 Feb 2024 20:49:09 +0000 Subject: [PATCH 10/12] add itemsizing to maintain marker size --- structuretoolkit/visualize.py | 1 + 1 file changed, 1 insertion(+) diff --git a/structuretoolkit/visualize.py b/structuretoolkit/visualize.py index 0ffea2333..8f9bcdb6e 100644 --- a/structuretoolkit/visualize.py +++ b/structuretoolkit/visualize.py @@ -227,6 +227,7 @@ def _plot3d_plotly( fig.update_layout(scene_camera=angle) fig.update_traces(marker=dict(line=dict(width=0.1, color="DarkSlateGrey"))) fig.update_scenes(aspectmode="data") + fig.update_layout(legend={'itemsizing': 'constant'}) return fig From b86dd00c3a4349fb1ca95654bde2e921e85b4f99 Mon Sep 17 00:00:00 2001 From: pyiron-runner Date: Thu, 8 Feb 2024 20:53:53 +0000 Subject: [PATCH 11/12] Format black --- structuretoolkit/visualize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structuretoolkit/visualize.py b/structuretoolkit/visualize.py index 8f9bcdb6e..dfb59c6f1 100644 --- a/structuretoolkit/visualize.py +++ b/structuretoolkit/visualize.py @@ -227,7 +227,7 @@ def _plot3d_plotly( fig.update_layout(scene_camera=angle) fig.update_traces(marker=dict(line=dict(width=0.1, color="DarkSlateGrey"))) fig.update_scenes(aspectmode="data") - fig.update_layout(legend={'itemsizing': 'constant'}) + fig.update_layout(legend={"itemsizing": "constant"}) return fig From 5528873e36209e7f98edd20da1e19a7efb4d92dc Mon Sep 17 00:00:00 2001 From: samwaseda Date: Thu, 8 Feb 2024 21:12:47 +0000 Subject: [PATCH 12/12] add messages --- tests/test_visualize.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_visualize.py b/tests/test_visualize.py index 4a6387978..c649865a8 100644 --- a/tests/test_visualize.py +++ b/tests/test_visualize.py @@ -30,9 +30,15 @@ def test_get_frame(self): dx, counts = np.unique( np.diff(frame, axis=-2).squeeze().astype(int), axis=0, return_counts=True ) - self.assertEqual(dx.ptp(), 1) - self.assertEqual(counts.min(), 4) - self.assertEqual(counts.max(), 4) + self.assertEqual( + dx.ptp(), 1, msg="Frames not drawn along the nearest edges" + ) + msg = ( + "There must be four lines along each direction" + + " (4 x [1, 0, 0], 4 x [0, 1, 0] and 4 x [0, 0, 1])" + ) + self.assertEqual(counts.min(), 4, msg=msg) + self.assertEqual(counts.max(), 4, msg=msg) if __name__ == "__main__":