crisp-ase 1.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- CRISP/__init__.py +99 -0
- CRISP/_version.py +1 -0
- CRISP/cli.py +41 -0
- CRISP/data_analysis/__init__.py +38 -0
- CRISP/data_analysis/clustering.py +838 -0
- CRISP/data_analysis/contact_coordination.py +915 -0
- CRISP/data_analysis/h_bond.py +772 -0
- CRISP/data_analysis/msd.py +1199 -0
- CRISP/data_analysis/prdf.py +404 -0
- CRISP/data_analysis/volumetric_atomic_density.py +527 -0
- CRISP/py.typed +1 -0
- CRISP/simulation_utility/__init__.py +31 -0
- CRISP/simulation_utility/atomic_indices.py +155 -0
- CRISP/simulation_utility/atomic_traj_linemap.py +278 -0
- CRISP/simulation_utility/error_analysis.py +254 -0
- CRISP/simulation_utility/interatomic_distances.py +200 -0
- CRISP/simulation_utility/subsampling.py +241 -0
- CRISP/tests/DataAnalysis/__init__.py +1 -0
- CRISP/tests/DataAnalysis/test_clustering_extended.py +212 -0
- CRISP/tests/DataAnalysis/test_contact_coordination.py +184 -0
- CRISP/tests/DataAnalysis/test_contact_coordination_extended.py +465 -0
- CRISP/tests/DataAnalysis/test_h_bond_complete.py +326 -0
- CRISP/tests/DataAnalysis/test_h_bond_extended.py +322 -0
- CRISP/tests/DataAnalysis/test_msd_complete.py +305 -0
- CRISP/tests/DataAnalysis/test_msd_extended.py +522 -0
- CRISP/tests/DataAnalysis/test_prdf.py +206 -0
- CRISP/tests/DataAnalysis/test_volumetric_atomic_density.py +463 -0
- CRISP/tests/SimulationUtility/__init__.py +1 -0
- CRISP/tests/SimulationUtility/test_atomic_traj_linemap.py +101 -0
- CRISP/tests/SimulationUtility/test_atomic_traj_linemap_extended.py +469 -0
- CRISP/tests/SimulationUtility/test_error_analysis_extended.py +151 -0
- CRISP/tests/SimulationUtility/test_interatomic_distances.py +223 -0
- CRISP/tests/SimulationUtility/test_subsampling.py +365 -0
- CRISP/tests/__init__.py +1 -0
- CRISP/tests/test_CRISP.py +28 -0
- CRISP/tests/test_cli.py +87 -0
- CRISP/tests/test_crisp_comprehensive.py +679 -0
- crisp_ase-1.1.2.dist-info/METADATA +141 -0
- crisp_ase-1.1.2.dist-info/RECORD +42 -0
- crisp_ase-1.1.2.dist-info/WHEEL +5 -0
- crisp_ase-1.1.2.dist-info/entry_points.txt +2 -0
- crisp_ase-1.1.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Tests for atomic trajectory linemap visualization module."""
|
|
2
|
+
import pytest
|
|
3
|
+
import os
|
|
4
|
+
import tempfile
|
|
5
|
+
import shutil
|
|
6
|
+
from CRISP.simulation_utility.atomic_traj_linemap import plot_atomic_trajectory
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def plot_output_dir():
|
|
11
|
+
"""Create temporary directory for plots."""
|
|
12
|
+
temp_dir = tempfile.mkdtemp()
|
|
13
|
+
yield temp_dir
|
|
14
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestAtomicTrajLinemapBasic:
|
|
18
|
+
"""Test basic trajectory plotting functionality."""
|
|
19
|
+
|
|
20
|
+
def test_plot_atomic_trajectory_module_import(self):
|
|
21
|
+
"""Test module can be imported."""
|
|
22
|
+
from CRISP.simulation_utility import atomic_traj_linemap
|
|
23
|
+
assert hasattr(atomic_traj_linemap, 'plot_atomic_trajectory')
|
|
24
|
+
assert callable(atomic_traj_linemap.plot_atomic_trajectory)
|
|
25
|
+
|
|
26
|
+
def test_invalid_trajectory_file(self, plot_output_dir):
|
|
27
|
+
"""Test with nonexistent trajectory file."""
|
|
28
|
+
with pytest.raises((FileNotFoundError, IOError, Exception)):
|
|
29
|
+
plot_atomic_trajectory(
|
|
30
|
+
traj_path="nonexistent_trajectory.xyz",
|
|
31
|
+
output_dir=plot_output_dir,
|
|
32
|
+
output_filename="test_plot.png"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def test_invalid_output_directory(self):
|
|
36
|
+
"""Test with trajectory file but try to write to invalid directory."""
|
|
37
|
+
# This should either create the directory or raise an error
|
|
38
|
+
invalid_dir = "/invalid/nonexistent/directory/path"
|
|
39
|
+
|
|
40
|
+
# Most implementations will either create the directory or raise an error
|
|
41
|
+
try:
|
|
42
|
+
# If trajectory exists, this might work or fail depending on implementation
|
|
43
|
+
with pytest.raises((OSError, PermissionError, Exception)):
|
|
44
|
+
plot_atomic_trajectory(
|
|
45
|
+
traj_path="test.xyz",
|
|
46
|
+
output_dir=invalid_dir,
|
|
47
|
+
output_filename="test.png"
|
|
48
|
+
)
|
|
49
|
+
except FileNotFoundError:
|
|
50
|
+
# This is also acceptable
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class TestAtomicTrajLinemapParameters:
|
|
55
|
+
"""Test parameter variations."""
|
|
56
|
+
|
|
57
|
+
@pytest.mark.parametrize("frame_skip", [1, 2, 5])
|
|
58
|
+
def test_frame_skip_parameter(self, frame_skip, plot_output_dir):
|
|
59
|
+
"""Test with different frame skip values."""
|
|
60
|
+
# This test would need a real trajectory file
|
|
61
|
+
pytest.skip("Requires actual trajectory data")
|
|
62
|
+
|
|
63
|
+
def test_plot_with_indices_list(self, plot_output_dir):
|
|
64
|
+
"""Test plotting with specific atom indices list."""
|
|
65
|
+
pytest.skip("Requires actual trajectory data")
|
|
66
|
+
|
|
67
|
+
def test_plot_with_custom_title(self, plot_output_dir):
|
|
68
|
+
"""Test plotting with custom title."""
|
|
69
|
+
pytest.skip("Requires actual trajectory data")
|
|
70
|
+
|
|
71
|
+
def test_plot_with_atom_size_scale(self, plot_output_dir):
|
|
72
|
+
"""Test plotting with custom atom size scale."""
|
|
73
|
+
pytest.skip("Requires actual trajectory data")
|
|
74
|
+
|
|
75
|
+
def test_plot_with_lines_enabled(self, plot_output_dir):
|
|
76
|
+
"""Test plotting with lines between atom positions."""
|
|
77
|
+
pytest.skip("Requires actual trajectory data")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class TestAtomicTrajLinemapEdgeCases:
|
|
81
|
+
"""Test edge cases."""
|
|
82
|
+
|
|
83
|
+
def test_empty_trajectory_file(self, plot_output_dir):
|
|
84
|
+
"""Test with empty trajectory file."""
|
|
85
|
+
pytest.skip("Requires actual trajectory data")
|
|
86
|
+
|
|
87
|
+
def test_single_frame_trajectory(self, plot_output_dir):
|
|
88
|
+
"""Test with single frame trajectory."""
|
|
89
|
+
pytest.skip("Requires actual trajectory data")
|
|
90
|
+
|
|
91
|
+
def test_large_frame_skip(self, plot_output_dir):
|
|
92
|
+
"""Test with frame skip larger than trajectory length."""
|
|
93
|
+
pytest.skip("Requires actual trajectory data")
|
|
94
|
+
|
|
95
|
+
def test_empty_indices_list(self, plot_output_dir):
|
|
96
|
+
"""Test with empty indices list."""
|
|
97
|
+
pytest.skip("Requires actual trajectory data")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if __name__ == '__main__':
|
|
101
|
+
pytest.main([__file__, '-v'])
|
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Comprehensive tests for atomic_traj_linemap module using real trajectory data.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
import os
|
|
7
|
+
import tempfile
|
|
8
|
+
import shutil
|
|
9
|
+
import numpy as np
|
|
10
|
+
from ase.io import read, write
|
|
11
|
+
from ase import Atoms
|
|
12
|
+
from CRISP.simulation_utility.atomic_traj_linemap import (
|
|
13
|
+
plot_atomic_trajectory,
|
|
14
|
+
VDW_RADII,
|
|
15
|
+
ELEMENT_COLORS
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def real_trajectory():
|
|
21
|
+
"""Return path to the actual trajectory file in the data folder."""
|
|
22
|
+
data_folder = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'data')
|
|
23
|
+
traj_path = os.path.join(data_folder, 'wrapped_traj.traj')
|
|
24
|
+
if os.path.exists(traj_path):
|
|
25
|
+
return traj_path
|
|
26
|
+
else:
|
|
27
|
+
pytest.skip("Real trajectory file not found")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.fixture
|
|
31
|
+
def temp_output_dir():
|
|
32
|
+
"""Create temporary output directory."""
|
|
33
|
+
temp_dir = tempfile.mkdtemp()
|
|
34
|
+
yield temp_dir
|
|
35
|
+
shutil.rmtree(temp_dir)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.fixture
|
|
39
|
+
def small_trajectory():
|
|
40
|
+
"""Create a small test trajectory."""
|
|
41
|
+
temp_dir = tempfile.mkdtemp()
|
|
42
|
+
traj_path = os.path.join(temp_dir, 'test.traj')
|
|
43
|
+
|
|
44
|
+
frames = []
|
|
45
|
+
for i in range(5):
|
|
46
|
+
atoms = Atoms('H2O', positions=[
|
|
47
|
+
[0 + 0.1*i, 0, 0],
|
|
48
|
+
[1 + 0.1*i, 0, 0],
|
|
49
|
+
[0.5 + 0.1*i, 0.5, 0]
|
|
50
|
+
])
|
|
51
|
+
atoms.cell = [10, 10, 10]
|
|
52
|
+
atoms.pbc = True
|
|
53
|
+
frames.append(atoms)
|
|
54
|
+
|
|
55
|
+
write(traj_path, frames)
|
|
56
|
+
yield traj_path
|
|
57
|
+
shutil.rmtree(temp_dir)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TestPlotAtomicTrajectoryBasic:
|
|
61
|
+
"""Test basic functionality of plot_atomic_trajectory."""
|
|
62
|
+
|
|
63
|
+
def test_plot_with_real_trajectory_all_atoms(self, real_trajectory, temp_output_dir):
|
|
64
|
+
"""Test plotting with real trajectory data."""
|
|
65
|
+
# Read trajectory to get number of atoms
|
|
66
|
+
traj = read(real_trajectory, index='0')
|
|
67
|
+
n_atoms = len(traj)
|
|
68
|
+
|
|
69
|
+
# Plot all atoms
|
|
70
|
+
fig = plot_atomic_trajectory(
|
|
71
|
+
real_trajectory,
|
|
72
|
+
indices_path=list(range(min(3, n_atoms))), # Plot first 3 atoms
|
|
73
|
+
output_dir=temp_output_dir,
|
|
74
|
+
output_filename='test_plot.html',
|
|
75
|
+
frame_skip=1,
|
|
76
|
+
show_plot=False
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
assert fig is not None
|
|
80
|
+
assert os.path.exists(os.path.join(temp_output_dir, 'test_plot.html'))
|
|
81
|
+
|
|
82
|
+
def test_plot_with_real_trajectory_frame_skip(self, real_trajectory, temp_output_dir):
|
|
83
|
+
"""Test frame skipping with real trajectory."""
|
|
84
|
+
traj = read(real_trajectory, index='0')
|
|
85
|
+
n_atoms = len(traj)
|
|
86
|
+
|
|
87
|
+
fig = plot_atomic_trajectory(
|
|
88
|
+
real_trajectory,
|
|
89
|
+
indices_path=list(range(min(2, n_atoms))),
|
|
90
|
+
output_dir=temp_output_dir,
|
|
91
|
+
frame_skip=2,
|
|
92
|
+
show_plot=False
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
assert fig is not None
|
|
96
|
+
|
|
97
|
+
def test_plot_with_custom_title(self, small_trajectory, temp_output_dir):
|
|
98
|
+
"""Test with custom plot title."""
|
|
99
|
+
fig = plot_atomic_trajectory(
|
|
100
|
+
small_trajectory,
|
|
101
|
+
indices_path=[0, 1],
|
|
102
|
+
output_dir=temp_output_dir,
|
|
103
|
+
plot_title="Custom Trajectory Title",
|
|
104
|
+
frame_skip=1,
|
|
105
|
+
show_plot=False
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
assert fig is not None
|
|
109
|
+
assert 'Custom Trajectory Title' in fig.layout.title.text
|
|
110
|
+
|
|
111
|
+
def test_plot_with_atom_size_scale(self, small_trajectory, temp_output_dir):
|
|
112
|
+
"""Test with different atom size scales."""
|
|
113
|
+
for scale in [0.5, 1.0, 2.0]:
|
|
114
|
+
fig = plot_atomic_trajectory(
|
|
115
|
+
small_trajectory,
|
|
116
|
+
indices_path=[0],
|
|
117
|
+
output_dir=temp_output_dir,
|
|
118
|
+
atom_size_scale=scale,
|
|
119
|
+
frame_skip=1,
|
|
120
|
+
show_plot=False
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
assert fig is not None
|
|
124
|
+
|
|
125
|
+
def test_plot_with_lines_enabled(self, small_trajectory, temp_output_dir):
|
|
126
|
+
"""Test trajectory with lines connecting positions."""
|
|
127
|
+
fig = plot_atomic_trajectory(
|
|
128
|
+
small_trajectory,
|
|
129
|
+
indices_path=[0, 1],
|
|
130
|
+
output_dir=temp_output_dir,
|
|
131
|
+
plot_lines=True,
|
|
132
|
+
frame_skip=1,
|
|
133
|
+
show_plot=False
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
assert fig is not None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class TestPlotAtomicTrajectoryIndices:
|
|
140
|
+
"""Test different ways of specifying atom indices."""
|
|
141
|
+
|
|
142
|
+
def test_plot_with_list_indices(self, small_trajectory, temp_output_dir):
|
|
143
|
+
"""Test with list of integer indices."""
|
|
144
|
+
fig = plot_atomic_trajectory(
|
|
145
|
+
small_trajectory,
|
|
146
|
+
indices_path=[0, 2],
|
|
147
|
+
output_dir=temp_output_dir,
|
|
148
|
+
frame_skip=1,
|
|
149
|
+
show_plot=False
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
assert fig is not None
|
|
153
|
+
|
|
154
|
+
def test_plot_with_numpy_file_indices(self, small_trajectory, temp_output_dir):
|
|
155
|
+
"""Test with numpy file containing indices."""
|
|
156
|
+
# Create numpy indices file
|
|
157
|
+
indices_file = os.path.join(temp_output_dir, 'indices.npy')
|
|
158
|
+
np.save(indices_file, np.array([0, 1]))
|
|
159
|
+
|
|
160
|
+
fig = plot_atomic_trajectory(
|
|
161
|
+
small_trajectory,
|
|
162
|
+
indices_path=indices_file,
|
|
163
|
+
output_dir=temp_output_dir,
|
|
164
|
+
frame_skip=1,
|
|
165
|
+
show_plot=False
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
assert fig is not None
|
|
169
|
+
|
|
170
|
+
def test_plot_with_single_atom(self, small_trajectory, temp_output_dir):
|
|
171
|
+
"""Test plotting single atom trajectory."""
|
|
172
|
+
fig = plot_atomic_trajectory(
|
|
173
|
+
small_trajectory,
|
|
174
|
+
indices_path=[1],
|
|
175
|
+
output_dir=temp_output_dir,
|
|
176
|
+
frame_skip=1,
|
|
177
|
+
show_plot=False
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
assert fig is not None
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class TestPlotAtomicTrajectoryOutputFiles:
|
|
184
|
+
"""Test output file creation and handling."""
|
|
185
|
+
|
|
186
|
+
def test_output_file_created(self, small_trajectory, temp_output_dir):
|
|
187
|
+
"""Test that output HTML file is created."""
|
|
188
|
+
output_file = 'custom_output.html'
|
|
189
|
+
fig = plot_atomic_trajectory(
|
|
190
|
+
small_trajectory,
|
|
191
|
+
indices_path=[0],
|
|
192
|
+
output_dir=temp_output_dir,
|
|
193
|
+
output_filename=output_file,
|
|
194
|
+
frame_skip=1,
|
|
195
|
+
show_plot=False
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
output_path = os.path.join(temp_output_dir, output_file)
|
|
199
|
+
assert os.path.exists(output_path)
|
|
200
|
+
|
|
201
|
+
# Check file is not empty
|
|
202
|
+
assert os.path.getsize(output_path) > 0
|
|
203
|
+
|
|
204
|
+
def test_output_directory_created(self, small_trajectory):
|
|
205
|
+
"""Test that output directory is created if it doesn't exist."""
|
|
206
|
+
temp_dir = tempfile.mkdtemp()
|
|
207
|
+
new_output_dir = os.path.join(temp_dir, 'new_subdir')
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
fig = plot_atomic_trajectory(
|
|
211
|
+
small_trajectory,
|
|
212
|
+
indices_path=[0],
|
|
213
|
+
output_dir=new_output_dir,
|
|
214
|
+
frame_skip=1,
|
|
215
|
+
show_plot=False
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
assert os.path.exists(new_output_dir)
|
|
219
|
+
finally:
|
|
220
|
+
shutil.rmtree(temp_dir)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class TestPlotAtomicTrajectoryFrameSkip:
|
|
224
|
+
"""Test frame skipping functionality."""
|
|
225
|
+
|
|
226
|
+
def test_frame_skip_variations(self, small_trajectory, temp_output_dir):
|
|
227
|
+
"""Test different frame skip values."""
|
|
228
|
+
for skip in [1, 2, 3]:
|
|
229
|
+
fig = plot_atomic_trajectory(
|
|
230
|
+
small_trajectory,
|
|
231
|
+
indices_path=[0],
|
|
232
|
+
output_dir=temp_output_dir,
|
|
233
|
+
frame_skip=skip,
|
|
234
|
+
show_plot=False
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
assert fig is not None
|
|
238
|
+
|
|
239
|
+
def test_large_frame_skip(self, real_trajectory, temp_output_dir):
|
|
240
|
+
"""Test with very large frame skip."""
|
|
241
|
+
traj = read(real_trajectory, index='0')
|
|
242
|
+
|
|
243
|
+
fig = plot_atomic_trajectory(
|
|
244
|
+
real_trajectory,
|
|
245
|
+
indices_path=[0],
|
|
246
|
+
output_dir=temp_output_dir,
|
|
247
|
+
frame_skip=100,
|
|
248
|
+
show_plot=False
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
assert fig is not None
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class TestPlotAtomicTrajectoryEdgeCases:
|
|
255
|
+
"""Test edge cases and error handling."""
|
|
256
|
+
|
|
257
|
+
def test_single_frame_trajectory(self, temp_output_dir):
|
|
258
|
+
"""Test with single frame trajectory."""
|
|
259
|
+
temp_dir = tempfile.mkdtemp()
|
|
260
|
+
traj_path = os.path.join(temp_dir, 'single_frame.traj')
|
|
261
|
+
|
|
262
|
+
try:
|
|
263
|
+
atoms = Atoms('H2O', positions=[[0, 0, 0], [1, 0, 0], [0.5, 0.5, 0]])
|
|
264
|
+
atoms.cell = [10, 10, 10]
|
|
265
|
+
write(traj_path, atoms)
|
|
266
|
+
|
|
267
|
+
fig = plot_atomic_trajectory(
|
|
268
|
+
traj_path,
|
|
269
|
+
indices_path=[0],
|
|
270
|
+
output_dir=temp_output_dir,
|
|
271
|
+
frame_skip=1,
|
|
272
|
+
show_plot=False
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
assert fig is not None
|
|
276
|
+
finally:
|
|
277
|
+
shutil.rmtree(temp_dir)
|
|
278
|
+
|
|
279
|
+
def test_empty_indices_list(self, small_trajectory, temp_output_dir):
|
|
280
|
+
"""Test with empty indices list."""
|
|
281
|
+
fig = plot_atomic_trajectory(
|
|
282
|
+
small_trajectory,
|
|
283
|
+
indices_path=[],
|
|
284
|
+
output_dir=temp_output_dir,
|
|
285
|
+
frame_skip=1,
|
|
286
|
+
show_plot=False
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
# Should still create plot with all atoms in first frame
|
|
290
|
+
assert fig is not None
|
|
291
|
+
|
|
292
|
+
def test_very_small_atom_size(self, small_trajectory, temp_output_dir):
|
|
293
|
+
"""Test with very small atom size scale."""
|
|
294
|
+
fig = plot_atomic_trajectory(
|
|
295
|
+
small_trajectory,
|
|
296
|
+
indices_path=[0],
|
|
297
|
+
output_dir=temp_output_dir,
|
|
298
|
+
atom_size_scale=0.1,
|
|
299
|
+
frame_skip=1,
|
|
300
|
+
show_plot=False
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
assert fig is not None
|
|
304
|
+
|
|
305
|
+
def test_very_large_atom_size(self, small_trajectory, temp_output_dir):
|
|
306
|
+
"""Test with very large atom size scale."""
|
|
307
|
+
fig = plot_atomic_trajectory(
|
|
308
|
+
small_trajectory,
|
|
309
|
+
indices_path=[0],
|
|
310
|
+
output_dir=temp_output_dir,
|
|
311
|
+
atom_size_scale=5.0,
|
|
312
|
+
frame_skip=1,
|
|
313
|
+
show_plot=False
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
assert fig is not None
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class TestVDWRadiiAndColors:
|
|
320
|
+
"""Test VDW radii and element color dictionaries."""
|
|
321
|
+
|
|
322
|
+
def test_vdw_radii_common_elements(self):
|
|
323
|
+
"""Test VDW radii for common elements."""
|
|
324
|
+
assert 'H' in VDW_RADII
|
|
325
|
+
assert 'C' in VDW_RADII
|
|
326
|
+
assert 'N' in VDW_RADII
|
|
327
|
+
assert 'O' in VDW_RADII
|
|
328
|
+
|
|
329
|
+
assert VDW_RADII['H'] > 0
|
|
330
|
+
assert VDW_RADII['O'] > 0
|
|
331
|
+
|
|
332
|
+
def test_element_colors_common_elements(self):
|
|
333
|
+
"""Test element colors for common elements."""
|
|
334
|
+
assert 'H' in ELEMENT_COLORS
|
|
335
|
+
assert 'C' in ELEMENT_COLORS
|
|
336
|
+
assert 'N' in ELEMENT_COLORS
|
|
337
|
+
assert 'O' in ELEMENT_COLORS
|
|
338
|
+
|
|
339
|
+
assert ELEMENT_COLORS['H'] == 'white'
|
|
340
|
+
assert ELEMENT_COLORS['O'] == 'red'
|
|
341
|
+
|
|
342
|
+
def test_vdw_radii_all_periods(self):
|
|
343
|
+
"""Test that VDW radii covers multiple periods."""
|
|
344
|
+
# Period 1-7 elements
|
|
345
|
+
test_elements = ['H', 'Li', 'Na', 'K', 'Rb', 'Cs', 'Fr']
|
|
346
|
+
for elem in test_elements:
|
|
347
|
+
assert elem in VDW_RADII
|
|
348
|
+
assert VDW_RADII[elem] > 0
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
class TestPlotAtomicTrajectoryIntegration:
|
|
352
|
+
"""Integration tests with real trajectory data."""
|
|
353
|
+
|
|
354
|
+
def test_complete_workflow(self, real_trajectory, temp_output_dir):
|
|
355
|
+
"""Test complete workflow from trajectory to visualization."""
|
|
356
|
+
# Read trajectory to get info
|
|
357
|
+
traj = read(real_trajectory, index='0')
|
|
358
|
+
n_atoms = len(traj)
|
|
359
|
+
|
|
360
|
+
# Select subset of atoms
|
|
361
|
+
indices = list(range(min(3, n_atoms)))
|
|
362
|
+
|
|
363
|
+
# Create visualization
|
|
364
|
+
fig = plot_atomic_trajectory(
|
|
365
|
+
real_trajectory,
|
|
366
|
+
indices_path=indices,
|
|
367
|
+
output_dir=temp_output_dir,
|
|
368
|
+
output_filename='workflow_test.html',
|
|
369
|
+
frame_skip=2,
|
|
370
|
+
plot_title='Integration Test',
|
|
371
|
+
atom_size_scale=1.5,
|
|
372
|
+
plot_lines=True,
|
|
373
|
+
show_plot=False
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
# Verify output
|
|
377
|
+
assert fig is not None
|
|
378
|
+
assert os.path.exists(os.path.join(temp_output_dir, 'workflow_test.html'))
|
|
379
|
+
|
|
380
|
+
# Verify figure has data
|
|
381
|
+
assert len(fig.data) > 0
|
|
382
|
+
|
|
383
|
+
def test_multiple_plots_same_directory(self, small_trajectory, temp_output_dir):
|
|
384
|
+
"""Test creating multiple plots in same directory."""
|
|
385
|
+
for i in range(3):
|
|
386
|
+
fig = plot_atomic_trajectory(
|
|
387
|
+
small_trajectory,
|
|
388
|
+
indices_path=[i % 3],
|
|
389
|
+
output_dir=temp_output_dir,
|
|
390
|
+
output_filename=f'plot_{i}.html',
|
|
391
|
+
frame_skip=1,
|
|
392
|
+
show_plot=False
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
assert fig is not None
|
|
396
|
+
|
|
397
|
+
# Check all files created
|
|
398
|
+
assert os.path.exists(os.path.join(temp_output_dir, 'plot_0.html'))
|
|
399
|
+
assert os.path.exists(os.path.join(temp_output_dir, 'plot_1.html'))
|
|
400
|
+
assert os.path.exists(os.path.join(temp_output_dir, 'plot_2.html'))
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
class TestPlotAtomicTrajectoryRealData:
|
|
404
|
+
"""Test specifically with real trajectory data from data folder."""
|
|
405
|
+
|
|
406
|
+
def test_oxygen_atoms_trajectory(self, real_trajectory, temp_output_dir):
|
|
407
|
+
"""Test plotting oxygen atom trajectories."""
|
|
408
|
+
# Read first frame to identify oxygen atoms
|
|
409
|
+
atoms = read(real_trajectory, index='0')
|
|
410
|
+
symbols = atoms.get_chemical_symbols()
|
|
411
|
+
|
|
412
|
+
# Find oxygen atoms
|
|
413
|
+
o_indices = [i for i, s in enumerate(symbols) if s == 'O']
|
|
414
|
+
|
|
415
|
+
if len(o_indices) > 0:
|
|
416
|
+
fig = plot_atomic_trajectory(
|
|
417
|
+
real_trajectory,
|
|
418
|
+
indices_path=o_indices[:min(3, len(o_indices))],
|
|
419
|
+
output_dir=temp_output_dir,
|
|
420
|
+
plot_title='Oxygen Trajectories',
|
|
421
|
+
frame_skip=5,
|
|
422
|
+
show_plot=False
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
assert fig is not None
|
|
426
|
+
|
|
427
|
+
def test_hydrogen_atoms_trajectory(self, real_trajectory, temp_output_dir):
|
|
428
|
+
"""Test plotting hydrogen atom trajectories."""
|
|
429
|
+
atoms = read(real_trajectory, index='0')
|
|
430
|
+
symbols = atoms.get_chemical_symbols()
|
|
431
|
+
|
|
432
|
+
# Find hydrogen atoms
|
|
433
|
+
h_indices = [i for i, s in enumerate(symbols) if s == 'H']
|
|
434
|
+
|
|
435
|
+
if len(h_indices) > 0:
|
|
436
|
+
fig = plot_atomic_trajectory(
|
|
437
|
+
real_trajectory,
|
|
438
|
+
indices_path=h_indices[:min(2, len(h_indices))],
|
|
439
|
+
output_dir=temp_output_dir,
|
|
440
|
+
plot_title='Hydrogen Trajectories',
|
|
441
|
+
frame_skip=10,
|
|
442
|
+
plot_lines=True,
|
|
443
|
+
show_plot=False
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
assert fig is not None
|
|
447
|
+
|
|
448
|
+
def test_real_trajectory_with_various_parameters(self, real_trajectory, temp_output_dir):
|
|
449
|
+
"""Test real trajectory with various parameter combinations."""
|
|
450
|
+
atoms = read(real_trajectory, index='0')
|
|
451
|
+
|
|
452
|
+
# Test with different combinations
|
|
453
|
+
configs = [
|
|
454
|
+
{'frame_skip': 1, 'atom_size_scale': 0.8, 'plot_lines': False},
|
|
455
|
+
{'frame_skip': 5, 'atom_size_scale': 1.2, 'plot_lines': True},
|
|
456
|
+
{'frame_skip': 10, 'atom_size_scale': 1.5, 'plot_lines': False},
|
|
457
|
+
]
|
|
458
|
+
|
|
459
|
+
for i, config in enumerate(configs):
|
|
460
|
+
fig = plot_atomic_trajectory(
|
|
461
|
+
real_trajectory,
|
|
462
|
+
indices_path=[0],
|
|
463
|
+
output_dir=temp_output_dir,
|
|
464
|
+
output_filename=f'config_{i}.html',
|
|
465
|
+
show_plot=False,
|
|
466
|
+
**config
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
assert fig is not None
|