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,206 @@
|
|
|
1
|
+
"""Extended tests for PRDF (Radial Distribution Function) module."""
|
|
2
|
+
import pytest
|
|
3
|
+
import numpy as np
|
|
4
|
+
import os
|
|
5
|
+
import tempfile
|
|
6
|
+
import shutil
|
|
7
|
+
from ase import Atoms
|
|
8
|
+
from ase.io import write
|
|
9
|
+
|
|
10
|
+
from CRISP.data_analysis.prdf import (
|
|
11
|
+
check_cell_and_r_max,
|
|
12
|
+
compute_pairwise_rdf,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestPRDFBasic:
|
|
17
|
+
"""Basic PRDF functionality tests."""
|
|
18
|
+
|
|
19
|
+
def test_check_cell_valid(self):
|
|
20
|
+
"""Test cell validation with valid cell."""
|
|
21
|
+
atoms = Atoms('H2', positions=[
|
|
22
|
+
[0.0, 0.0, 0.0],
|
|
23
|
+
[0.74, 0.0, 0.0]
|
|
24
|
+
])
|
|
25
|
+
atoms.set_cell([10, 10, 10])
|
|
26
|
+
atoms.set_pbc([True, True, True])
|
|
27
|
+
|
|
28
|
+
# Should not raise for valid cell
|
|
29
|
+
try:
|
|
30
|
+
check_cell_and_r_max(atoms, 4.0)
|
|
31
|
+
except ValueError:
|
|
32
|
+
pytest.fail("Should not raise for valid cell")
|
|
33
|
+
|
|
34
|
+
def test_check_cell_too_small(self):
|
|
35
|
+
"""Test cell validation with cell too small for rmax."""
|
|
36
|
+
atoms = Atoms('H2', positions=[
|
|
37
|
+
[0.0, 0.0, 0.0],
|
|
38
|
+
[0.74, 0.0, 0.0]
|
|
39
|
+
])
|
|
40
|
+
atoms.set_cell([2, 2, 2])
|
|
41
|
+
atoms.set_pbc([True, True, True])
|
|
42
|
+
|
|
43
|
+
# Should raise for cell too small
|
|
44
|
+
with pytest.raises(ValueError):
|
|
45
|
+
check_cell_and_r_max(atoms, 5.0)
|
|
46
|
+
|
|
47
|
+
def test_check_cell_undefined(self):
|
|
48
|
+
"""Test cell validation with undefined cell."""
|
|
49
|
+
atoms = Atoms('H2', positions=[
|
|
50
|
+
[0.0, 0.0, 0.0],
|
|
51
|
+
[0.74, 0.0, 0.0]
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
# Should raise for undefined cell
|
|
55
|
+
with pytest.raises(ValueError):
|
|
56
|
+
check_cell_and_r_max(atoms, 2.0)
|
|
57
|
+
|
|
58
|
+
def test_compute_pairwise_rdf_basic(self):
|
|
59
|
+
"""Test basic pairwise RDF calculation."""
|
|
60
|
+
atoms = Atoms('H2O', positions=[
|
|
61
|
+
[0.0, 0.0, 0.0],
|
|
62
|
+
[0.96, 0.0, 0.0],
|
|
63
|
+
[0.24, 0.93, 0.0]
|
|
64
|
+
])
|
|
65
|
+
atoms.set_cell([10, 10, 10])
|
|
66
|
+
atoms.set_pbc([True, True, True])
|
|
67
|
+
|
|
68
|
+
# compute_pairwise_rdf uses rmax and nbins (not r_max and dr)
|
|
69
|
+
# nbins = rmax / dr, so for r_max=5.0 and dr=0.1, nbins=50
|
|
70
|
+
g_r, r = compute_pairwise_rdf(
|
|
71
|
+
atoms=atoms,
|
|
72
|
+
ref_indices=[0],
|
|
73
|
+
target_indices=[1, 2],
|
|
74
|
+
rmax=5.0,
|
|
75
|
+
nbins=50
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
assert len(r) > 0
|
|
79
|
+
assert len(g_r) > 0
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class TestPRDFParametrized:
|
|
83
|
+
"""Test PRDF with parameter variations."""
|
|
84
|
+
|
|
85
|
+
@pytest.mark.parametrize("r_max", [3.0, 5.0, 8.0])
|
|
86
|
+
def test_rdf_different_r_max(self, r_max):
|
|
87
|
+
"""Test RDF with different maximum radius."""
|
|
88
|
+
atoms = Atoms('H2', positions=[
|
|
89
|
+
[0.0, 0.0, 0.0],
|
|
90
|
+
[0.74, 0.0, 0.0]
|
|
91
|
+
])
|
|
92
|
+
atoms.set_cell([20, 20, 20])
|
|
93
|
+
atoms.set_pbc([True, True, True])
|
|
94
|
+
|
|
95
|
+
# nbins = rmax / dr, so for dr=0.1
|
|
96
|
+
nbins = int(r_max / 0.1)
|
|
97
|
+
g_r, r = compute_pairwise_rdf(
|
|
98
|
+
atoms=atoms,
|
|
99
|
+
ref_indices=[0],
|
|
100
|
+
target_indices=[1],
|
|
101
|
+
rmax=r_max,
|
|
102
|
+
nbins=nbins
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
assert r[-1] <= r_max + 0.1
|
|
106
|
+
|
|
107
|
+
@pytest.mark.parametrize("dr", [0.05, 0.1, 0.2])
|
|
108
|
+
def test_rdf_different_dr(self, dr):
|
|
109
|
+
"""Test RDF with different bin size."""
|
|
110
|
+
atoms = Atoms('H2', positions=[
|
|
111
|
+
[0.0, 0.0, 0.0],
|
|
112
|
+
[0.74, 0.0, 0.0]
|
|
113
|
+
])
|
|
114
|
+
atoms.set_cell([10, 10, 10])
|
|
115
|
+
atoms.set_pbc([True, True, True])
|
|
116
|
+
|
|
117
|
+
# nbins = rmax / dr
|
|
118
|
+
rmax = 5.0
|
|
119
|
+
nbins = int(rmax / dr)
|
|
120
|
+
g_r, r = compute_pairwise_rdf(
|
|
121
|
+
atoms=atoms,
|
|
122
|
+
ref_indices=[0],
|
|
123
|
+
target_indices=[1],
|
|
124
|
+
rmax=rmax,
|
|
125
|
+
nbins=nbins
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
assert len(r) > 0
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class TestPRDFEdgeCases:
|
|
132
|
+
"""Test PRDF edge cases."""
|
|
133
|
+
|
|
134
|
+
def test_rdf_single_atom(self):
|
|
135
|
+
"""Test RDF with single atom."""
|
|
136
|
+
atoms = Atoms('H', positions=[[0.0, 0.0, 0.0]])
|
|
137
|
+
atoms.set_cell([10, 10, 10])
|
|
138
|
+
atoms.set_pbc([True, True, True])
|
|
139
|
+
|
|
140
|
+
# Should handle gracefully - empty target indices
|
|
141
|
+
try:
|
|
142
|
+
g_r, r = compute_pairwise_rdf(
|
|
143
|
+
atoms=atoms,
|
|
144
|
+
ref_indices=[0],
|
|
145
|
+
target_indices=[],
|
|
146
|
+
rmax=5.0,
|
|
147
|
+
nbins=50
|
|
148
|
+
)
|
|
149
|
+
assert len(r) >= 0
|
|
150
|
+
except (ValueError, ZeroDivisionError):
|
|
151
|
+
pass
|
|
152
|
+
|
|
153
|
+
def test_rdf_empty_target(self):
|
|
154
|
+
"""Test RDF with empty target indices."""
|
|
155
|
+
atoms = Atoms('H2', positions=[
|
|
156
|
+
[0.0, 0.0, 0.0],
|
|
157
|
+
[0.74, 0.0, 0.0]
|
|
158
|
+
])
|
|
159
|
+
atoms.set_cell([10, 10, 10])
|
|
160
|
+
atoms.set_pbc([True, True, True])
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
g_r, r = compute_pairwise_rdf(
|
|
164
|
+
atoms=atoms,
|
|
165
|
+
ref_indices=[0],
|
|
166
|
+
target_indices=[],
|
|
167
|
+
rmax=5.0,
|
|
168
|
+
nbins=50
|
|
169
|
+
)
|
|
170
|
+
# Empty target should return zeros or handle gracefully
|
|
171
|
+
assert len(r) >= 0
|
|
172
|
+
except ValueError:
|
|
173
|
+
pass
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class TestPRDFIntegration:
|
|
177
|
+
"""Integration tests for PRDF."""
|
|
178
|
+
|
|
179
|
+
def test_rdf_with_trajectory_mock(self):
|
|
180
|
+
"""Test RDF calculation from mock trajectory."""
|
|
181
|
+
temp_dir = tempfile.mkdtemp()
|
|
182
|
+
try:
|
|
183
|
+
atoms = Atoms('H2O', positions=[
|
|
184
|
+
[0.0, 0.0, 0.0],
|
|
185
|
+
[0.96, 0.0, 0.0],
|
|
186
|
+
[0.24, 0.93, 0.0]
|
|
187
|
+
])
|
|
188
|
+
atoms.set_cell([10, 10, 10])
|
|
189
|
+
atoms.set_pbc([True, True, True])
|
|
190
|
+
|
|
191
|
+
g_r, r = compute_pairwise_rdf(
|
|
192
|
+
atoms=atoms,
|
|
193
|
+
ref_indices=[0],
|
|
194
|
+
target_indices=[1, 2],
|
|
195
|
+
rmax=5.0,
|
|
196
|
+
nbins=50
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
assert len(r) > 0
|
|
200
|
+
assert len(g_r) > 0
|
|
201
|
+
finally:
|
|
202
|
+
shutil.rmtree(temp_dir)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
if __name__ == '__main__':
|
|
206
|
+
pytest.main([__file__, '-v'])
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
"""Extended tests for volumetric_atomic_density module."""
|
|
2
|
+
import pytest
|
|
3
|
+
import numpy as np
|
|
4
|
+
import os
|
|
5
|
+
import tempfile
|
|
6
|
+
import shutil
|
|
7
|
+
from ase import Atoms
|
|
8
|
+
from ase.io import write
|
|
9
|
+
|
|
10
|
+
from CRISP.data_analysis.volumetric_atomic_density import (
|
|
11
|
+
create_density_map,
|
|
12
|
+
VDW_RADII,
|
|
13
|
+
ELEMENT_COLORS,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestVolumetricDensityBasic:
|
|
18
|
+
"""Basic volumetric density functionality tests."""
|
|
19
|
+
|
|
20
|
+
def test_vdw_radii_dict_exists(self):
|
|
21
|
+
"""Test that VDW_RADII dictionary is properly defined."""
|
|
22
|
+
assert isinstance(VDW_RADII, dict)
|
|
23
|
+
assert len(VDW_RADII) > 100
|
|
24
|
+
assert 'H' in VDW_RADII
|
|
25
|
+
assert 'C' in VDW_RADII
|
|
26
|
+
assert 'O' in VDW_RADII
|
|
27
|
+
assert VDW_RADII['H'] == 1.20
|
|
28
|
+
assert VDW_RADII['C'] == 1.70
|
|
29
|
+
|
|
30
|
+
def test_element_colors_dict_exists(self):
|
|
31
|
+
"""Test that ELEMENT_COLORS dictionary is properly defined."""
|
|
32
|
+
assert isinstance(ELEMENT_COLORS, dict)
|
|
33
|
+
assert len(ELEMENT_COLORS) > 20
|
|
34
|
+
assert 'H' in ELEMENT_COLORS
|
|
35
|
+
assert 'C' in ELEMENT_COLORS
|
|
36
|
+
assert 'O' in ELEMENT_COLORS
|
|
37
|
+
|
|
38
|
+
def test_create_density_map_basic(self):
|
|
39
|
+
"""Test basic density map creation."""
|
|
40
|
+
temp_dir = tempfile.mkdtemp()
|
|
41
|
+
try:
|
|
42
|
+
# Create a simple trajectory
|
|
43
|
+
traj_file = os.path.join(temp_dir, 'test.traj')
|
|
44
|
+
indices_file = os.path.join(temp_dir, 'indices.npy')
|
|
45
|
+
|
|
46
|
+
atoms = Atoms('H2O', positions=[
|
|
47
|
+
[0.0, 0.0, 0.0],
|
|
48
|
+
[0.96, 0.0, 0.0],
|
|
49
|
+
[0.24, 0.93, 0.0]
|
|
50
|
+
])
|
|
51
|
+
atoms.set_cell([10, 10, 10])
|
|
52
|
+
atoms.set_pbc([True, True, True])
|
|
53
|
+
|
|
54
|
+
# Write trajectory with multiple frames
|
|
55
|
+
write(traj_file, atoms)
|
|
56
|
+
write(traj_file, atoms)
|
|
57
|
+
|
|
58
|
+
# Save indices
|
|
59
|
+
np.save(indices_file, np.array([0, 1]))
|
|
60
|
+
|
|
61
|
+
# Create density map
|
|
62
|
+
fig = create_density_map(
|
|
63
|
+
traj_path=traj_file,
|
|
64
|
+
indices_path=indices_file,
|
|
65
|
+
frame_skip=1,
|
|
66
|
+
nbins=20,
|
|
67
|
+
output_dir=temp_dir
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
assert fig is not None
|
|
71
|
+
assert hasattr(fig, 'add_trace')
|
|
72
|
+
finally:
|
|
73
|
+
shutil.rmtree(temp_dir)
|
|
74
|
+
|
|
75
|
+
def test_create_density_map_with_custom_parameters(self):
|
|
76
|
+
"""Test density map creation with custom parameters."""
|
|
77
|
+
temp_dir = tempfile.mkdtemp()
|
|
78
|
+
try:
|
|
79
|
+
traj_file = os.path.join(temp_dir, 'test.traj')
|
|
80
|
+
indices_file = os.path.join(temp_dir, 'indices.npy')
|
|
81
|
+
|
|
82
|
+
atoms = Atoms('H2O', positions=[
|
|
83
|
+
[0.0, 0.0, 0.0],
|
|
84
|
+
[0.96, 0.0, 0.0],
|
|
85
|
+
[0.24, 0.93, 0.0]
|
|
86
|
+
])
|
|
87
|
+
atoms.set_cell([10, 10, 10])
|
|
88
|
+
atoms.set_pbc([True, True, True])
|
|
89
|
+
|
|
90
|
+
write(traj_file, atoms)
|
|
91
|
+
write(traj_file, atoms)
|
|
92
|
+
|
|
93
|
+
np.save(indices_file, np.array([1, 2]))
|
|
94
|
+
|
|
95
|
+
fig = create_density_map(
|
|
96
|
+
traj_path=traj_file,
|
|
97
|
+
indices_path=indices_file,
|
|
98
|
+
frame_skip=1,
|
|
99
|
+
threshold=0.1,
|
|
100
|
+
absolute_threshold=False,
|
|
101
|
+
opacity=0.5,
|
|
102
|
+
atom_size_scale=2.0,
|
|
103
|
+
nbins=30,
|
|
104
|
+
colorscale='Viridis',
|
|
105
|
+
plot_title='Custom Density Map',
|
|
106
|
+
output_dir=temp_dir,
|
|
107
|
+
output_file='custom_density.html'
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
assert fig is not None
|
|
111
|
+
output_file = os.path.join(temp_dir, 'custom_density.html')
|
|
112
|
+
assert os.path.exists(output_file)
|
|
113
|
+
finally:
|
|
114
|
+
shutil.rmtree(temp_dir)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class TestVolumetricDensityAbsoluteThreshold:
|
|
118
|
+
"""Test absolute threshold functionality."""
|
|
119
|
+
|
|
120
|
+
@pytest.mark.parametrize("abs_threshold", [0.0, 1.0, 5.0, 10.0])
|
|
121
|
+
def test_absolute_threshold_variations(self, abs_threshold):
|
|
122
|
+
"""Test density map with different absolute thresholds."""
|
|
123
|
+
temp_dir = tempfile.mkdtemp()
|
|
124
|
+
try:
|
|
125
|
+
traj_file = os.path.join(temp_dir, 'test.traj')
|
|
126
|
+
indices_file = os.path.join(temp_dir, 'indices.npy')
|
|
127
|
+
|
|
128
|
+
atoms = Atoms('H2O', positions=[
|
|
129
|
+
[0.0, 0.0, 0.0],
|
|
130
|
+
[0.96, 0.0, 0.0],
|
|
131
|
+
[0.24, 0.93, 0.0]
|
|
132
|
+
])
|
|
133
|
+
atoms.set_cell([10, 10, 10])
|
|
134
|
+
atoms.set_pbc([True, True, True])
|
|
135
|
+
|
|
136
|
+
write(traj_file, atoms)
|
|
137
|
+
write(traj_file, atoms)
|
|
138
|
+
|
|
139
|
+
np.save(indices_file, np.array([0]))
|
|
140
|
+
|
|
141
|
+
fig = create_density_map(
|
|
142
|
+
traj_path=traj_file,
|
|
143
|
+
indices_path=indices_file,
|
|
144
|
+
frame_skip=1,
|
|
145
|
+
threshold=abs_threshold,
|
|
146
|
+
absolute_threshold=True,
|
|
147
|
+
nbins=20,
|
|
148
|
+
output_dir=temp_dir
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
assert fig is not None
|
|
152
|
+
finally:
|
|
153
|
+
shutil.rmtree(temp_dir)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class TestVolumetricDensityOpacity:
|
|
157
|
+
"""Test opacity parameter."""
|
|
158
|
+
|
|
159
|
+
@pytest.mark.parametrize("opacity", [0.0, 0.2, 0.5, 0.8, 1.0])
|
|
160
|
+
def test_opacity_variations(self, opacity):
|
|
161
|
+
"""Test density map with different opacity values."""
|
|
162
|
+
temp_dir = tempfile.mkdtemp()
|
|
163
|
+
try:
|
|
164
|
+
traj_file = os.path.join(temp_dir, 'test.traj')
|
|
165
|
+
indices_file = os.path.join(temp_dir, 'indices.npy')
|
|
166
|
+
|
|
167
|
+
atoms = Atoms('H2O', positions=[
|
|
168
|
+
[0.0, 0.0, 0.0],
|
|
169
|
+
[0.96, 0.0, 0.0],
|
|
170
|
+
[0.24, 0.93, 0.0]
|
|
171
|
+
])
|
|
172
|
+
atoms.set_cell([10, 10, 10])
|
|
173
|
+
atoms.set_pbc([True, True, True])
|
|
174
|
+
|
|
175
|
+
write(traj_file, atoms)
|
|
176
|
+
write(traj_file, atoms)
|
|
177
|
+
|
|
178
|
+
np.save(indices_file, np.array([0, 1]))
|
|
179
|
+
|
|
180
|
+
fig = create_density_map(
|
|
181
|
+
traj_path=traj_file,
|
|
182
|
+
indices_path=indices_file,
|
|
183
|
+
frame_skip=1,
|
|
184
|
+
opacity=opacity,
|
|
185
|
+
nbins=20,
|
|
186
|
+
output_dir=temp_dir
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
assert fig is not None
|
|
190
|
+
finally:
|
|
191
|
+
shutil.rmtree(temp_dir)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class TestVolumetricDensityBins:
|
|
195
|
+
"""Test number of bins parameter."""
|
|
196
|
+
|
|
197
|
+
@pytest.mark.parametrize("nbins", [10, 20, 40, 50])
|
|
198
|
+
def test_nbins_variations(self, nbins):
|
|
199
|
+
"""Test density map with different bin counts."""
|
|
200
|
+
temp_dir = tempfile.mkdtemp()
|
|
201
|
+
try:
|
|
202
|
+
traj_file = os.path.join(temp_dir, 'test.traj')
|
|
203
|
+
indices_file = os.path.join(temp_dir, 'indices.npy')
|
|
204
|
+
|
|
205
|
+
atoms = Atoms('H2O', positions=[
|
|
206
|
+
[0.0, 0.0, 0.0],
|
|
207
|
+
[0.96, 0.0, 0.0],
|
|
208
|
+
[0.24, 0.93, 0.0]
|
|
209
|
+
])
|
|
210
|
+
atoms.set_cell([10, 10, 10])
|
|
211
|
+
atoms.set_pbc([True, True, True])
|
|
212
|
+
|
|
213
|
+
write(traj_file, atoms)
|
|
214
|
+
write(traj_file, atoms)
|
|
215
|
+
|
|
216
|
+
np.save(indices_file, np.array([0, 1]))
|
|
217
|
+
|
|
218
|
+
fig = create_density_map(
|
|
219
|
+
traj_path=traj_file,
|
|
220
|
+
indices_path=indices_file,
|
|
221
|
+
frame_skip=1,
|
|
222
|
+
nbins=nbins,
|
|
223
|
+
output_dir=temp_dir
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
assert fig is not None
|
|
227
|
+
finally:
|
|
228
|
+
shutil.rmtree(temp_dir)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class TestVolumetricDensitySaveDensity:
|
|
232
|
+
"""Test saving density data functionality."""
|
|
233
|
+
|
|
234
|
+
def test_save_density_data(self):
|
|
235
|
+
"""Test saving density data to file."""
|
|
236
|
+
temp_dir = tempfile.mkdtemp()
|
|
237
|
+
try:
|
|
238
|
+
traj_file = os.path.join(temp_dir, 'test.traj')
|
|
239
|
+
indices_file = os.path.join(temp_dir, 'indices.npy')
|
|
240
|
+
density_file = os.path.join(temp_dir, 'density.npz')
|
|
241
|
+
|
|
242
|
+
atoms = Atoms('H2O', positions=[
|
|
243
|
+
[0.0, 0.0, 0.0],
|
|
244
|
+
[0.96, 0.0, 0.0],
|
|
245
|
+
[0.24, 0.93, 0.0]
|
|
246
|
+
])
|
|
247
|
+
atoms.set_cell([10, 10, 10])
|
|
248
|
+
atoms.set_pbc([True, True, True])
|
|
249
|
+
|
|
250
|
+
write(traj_file, atoms)
|
|
251
|
+
write(traj_file, atoms)
|
|
252
|
+
|
|
253
|
+
np.save(indices_file, np.array([0, 1]))
|
|
254
|
+
|
|
255
|
+
fig = create_density_map(
|
|
256
|
+
traj_path=traj_file,
|
|
257
|
+
indices_path=indices_file,
|
|
258
|
+
frame_skip=1,
|
|
259
|
+
nbins=20,
|
|
260
|
+
save_density=True,
|
|
261
|
+
density_output_file='density.npz',
|
|
262
|
+
output_dir=temp_dir
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
assert fig is not None
|
|
266
|
+
assert os.path.exists(density_file)
|
|
267
|
+
|
|
268
|
+
# Load and verify density data
|
|
269
|
+
data = np.load(density_file)
|
|
270
|
+
assert 'density' in data
|
|
271
|
+
assert 'edges' in data
|
|
272
|
+
assert 'cell' in data
|
|
273
|
+
assert 'nbins' in data
|
|
274
|
+
assert 'selected_indices' in data
|
|
275
|
+
finally:
|
|
276
|
+
shutil.rmtree(temp_dir)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class TestVolumetricDensityProjections:
|
|
280
|
+
"""Test projection functionality."""
|
|
281
|
+
|
|
282
|
+
def test_projections_enabled(self):
|
|
283
|
+
"""Test density map with 2D projections enabled."""
|
|
284
|
+
temp_dir = tempfile.mkdtemp()
|
|
285
|
+
try:
|
|
286
|
+
traj_file = os.path.join(temp_dir, 'test.traj')
|
|
287
|
+
indices_file = os.path.join(temp_dir, 'indices.npy')
|
|
288
|
+
|
|
289
|
+
atoms = Atoms('H2O', positions=[
|
|
290
|
+
[0.0, 0.0, 0.0],
|
|
291
|
+
[0.96, 0.0, 0.0],
|
|
292
|
+
[0.24, 0.93, 0.0]
|
|
293
|
+
])
|
|
294
|
+
atoms.set_cell([10, 10, 10])
|
|
295
|
+
atoms.set_pbc([True, True, True])
|
|
296
|
+
|
|
297
|
+
write(traj_file, atoms)
|
|
298
|
+
write(traj_file, atoms)
|
|
299
|
+
|
|
300
|
+
np.save(indices_file, np.array([0, 1]))
|
|
301
|
+
|
|
302
|
+
fig = create_density_map(
|
|
303
|
+
traj_path=traj_file,
|
|
304
|
+
indices_path=indices_file,
|
|
305
|
+
frame_skip=1,
|
|
306
|
+
nbins=20,
|
|
307
|
+
show_projections=True,
|
|
308
|
+
projection_opacity=0.7,
|
|
309
|
+
projection_offset=2.0,
|
|
310
|
+
output_dir=temp_dir
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
assert fig is not None
|
|
314
|
+
finally:
|
|
315
|
+
shutil.rmtree(temp_dir)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class TestVolumetricDensityColorscales:
|
|
319
|
+
"""Test different colorscale options."""
|
|
320
|
+
|
|
321
|
+
@pytest.mark.parametrize("colorscale", ['Plasma', 'Viridis', 'Blues', 'Reds'])
|
|
322
|
+
def test_colorscale_variations(self, colorscale):
|
|
323
|
+
"""Test density map with different colorscales."""
|
|
324
|
+
temp_dir = tempfile.mkdtemp()
|
|
325
|
+
try:
|
|
326
|
+
traj_file = os.path.join(temp_dir, 'test.traj')
|
|
327
|
+
indices_file = os.path.join(temp_dir, 'indices.npy')
|
|
328
|
+
|
|
329
|
+
atoms = Atoms('H2O', positions=[
|
|
330
|
+
[0.0, 0.0, 0.0],
|
|
331
|
+
[0.96, 0.0, 0.0],
|
|
332
|
+
[0.24, 0.93, 0.0]
|
|
333
|
+
])
|
|
334
|
+
atoms.set_cell([10, 10, 10])
|
|
335
|
+
atoms.set_pbc([True, True, True])
|
|
336
|
+
|
|
337
|
+
write(traj_file, atoms)
|
|
338
|
+
write(traj_file, atoms)
|
|
339
|
+
|
|
340
|
+
np.save(indices_file, np.array([0, 1]))
|
|
341
|
+
|
|
342
|
+
fig = create_density_map(
|
|
343
|
+
traj_path=traj_file,
|
|
344
|
+
indices_path=indices_file,
|
|
345
|
+
frame_skip=1,
|
|
346
|
+
nbins=20,
|
|
347
|
+
colorscale=colorscale,
|
|
348
|
+
output_dir=temp_dir
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
assert fig is not None
|
|
352
|
+
finally:
|
|
353
|
+
shutil.rmtree(temp_dir)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class TestVolumetricDensityEdgeCases:
|
|
357
|
+
"""Test edge cases and error handling."""
|
|
358
|
+
|
|
359
|
+
def test_single_atom_system(self):
|
|
360
|
+
"""Test density map with single atom."""
|
|
361
|
+
temp_dir = tempfile.mkdtemp()
|
|
362
|
+
try:
|
|
363
|
+
traj_file = os.path.join(temp_dir, 'test.traj')
|
|
364
|
+
indices_file = os.path.join(temp_dir, 'indices.npy')
|
|
365
|
+
|
|
366
|
+
atoms = Atoms('H', positions=[[0.0, 0.0, 0.0]])
|
|
367
|
+
atoms.set_cell([10, 10, 10])
|
|
368
|
+
atoms.set_pbc([True, True, True])
|
|
369
|
+
|
|
370
|
+
write(traj_file, atoms)
|
|
371
|
+
write(traj_file, atoms)
|
|
372
|
+
|
|
373
|
+
np.save(indices_file, np.array([0]))
|
|
374
|
+
|
|
375
|
+
fig = create_density_map(
|
|
376
|
+
traj_path=traj_file,
|
|
377
|
+
indices_path=indices_file,
|
|
378
|
+
frame_skip=1,
|
|
379
|
+
nbins=20,
|
|
380
|
+
output_dir=temp_dir
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
assert fig is not None
|
|
384
|
+
finally:
|
|
385
|
+
shutil.rmtree(temp_dir)
|
|
386
|
+
|
|
387
|
+
def test_large_cell(self):
|
|
388
|
+
"""Test density map with large unit cell."""
|
|
389
|
+
temp_dir = tempfile.mkdtemp()
|
|
390
|
+
try:
|
|
391
|
+
traj_file = os.path.join(temp_dir, 'test.traj')
|
|
392
|
+
indices_file = os.path.join(temp_dir, 'indices.npy')
|
|
393
|
+
|
|
394
|
+
atoms = Atoms('H2O', positions=[
|
|
395
|
+
[0.0, 0.0, 0.0],
|
|
396
|
+
[0.96, 0.0, 0.0],
|
|
397
|
+
[0.24, 0.93, 0.0]
|
|
398
|
+
])
|
|
399
|
+
atoms.set_cell([50, 50, 50])
|
|
400
|
+
atoms.set_pbc([True, True, True])
|
|
401
|
+
|
|
402
|
+
write(traj_file, atoms)
|
|
403
|
+
write(traj_file, atoms)
|
|
404
|
+
|
|
405
|
+
np.save(indices_file, np.array([0, 1]))
|
|
406
|
+
|
|
407
|
+
fig = create_density_map(
|
|
408
|
+
traj_path=traj_file,
|
|
409
|
+
indices_path=indices_file,
|
|
410
|
+
frame_skip=1,
|
|
411
|
+
nbins=20,
|
|
412
|
+
output_dir=temp_dir
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
assert fig is not None
|
|
416
|
+
finally:
|
|
417
|
+
shutil.rmtree(temp_dir)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class TestVolumetricDensityIntegration:
|
|
421
|
+
"""Integration tests for volumetric density."""
|
|
422
|
+
|
|
423
|
+
def test_complete_workflow(self):
|
|
424
|
+
"""Test complete density analysis workflow."""
|
|
425
|
+
temp_dir = tempfile.mkdtemp()
|
|
426
|
+
try:
|
|
427
|
+
traj_file = os.path.join(temp_dir, 'workflow.traj')
|
|
428
|
+
indices_file = os.path.join(temp_dir, 'workflow_indices.npy')
|
|
429
|
+
|
|
430
|
+
# Create multi-frame trajectory
|
|
431
|
+
for i in range(5):
|
|
432
|
+
atoms = Atoms('H2O', positions=[
|
|
433
|
+
[0.0 + i*0.1, 0.0, 0.0],
|
|
434
|
+
[0.96, 0.0, 0.0],
|
|
435
|
+
[0.24, 0.93, 0.0]
|
|
436
|
+
])
|
|
437
|
+
atoms.set_cell([10, 10, 10])
|
|
438
|
+
atoms.set_pbc([True, True, True])
|
|
439
|
+
write(traj_file, atoms, append=(i > 0))
|
|
440
|
+
|
|
441
|
+
np.save(indices_file, np.array([0, 1]))
|
|
442
|
+
|
|
443
|
+
fig = create_density_map(
|
|
444
|
+
traj_path=traj_file,
|
|
445
|
+
indices_path=indices_file,
|
|
446
|
+
frame_skip=1,
|
|
447
|
+
nbins=20,
|
|
448
|
+
save_density=True,
|
|
449
|
+
output_dir=temp_dir,
|
|
450
|
+
show_projections=True
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
assert fig is not None
|
|
454
|
+
|
|
455
|
+
# Verify output file was created
|
|
456
|
+
html_file = os.path.join(temp_dir, 'workflow_density_map.html')
|
|
457
|
+
assert os.path.exists(html_file)
|
|
458
|
+
finally:
|
|
459
|
+
shutil.rmtree(temp_dir)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
if __name__ == '__main__':
|
|
463
|
+
pytest.main([__file__, '-v'])
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CRISP SimulationUtility tests package."""
|