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.
Files changed (42) hide show
  1. CRISP/__init__.py +99 -0
  2. CRISP/_version.py +1 -0
  3. CRISP/cli.py +41 -0
  4. CRISP/data_analysis/__init__.py +38 -0
  5. CRISP/data_analysis/clustering.py +838 -0
  6. CRISP/data_analysis/contact_coordination.py +915 -0
  7. CRISP/data_analysis/h_bond.py +772 -0
  8. CRISP/data_analysis/msd.py +1199 -0
  9. CRISP/data_analysis/prdf.py +404 -0
  10. CRISP/data_analysis/volumetric_atomic_density.py +527 -0
  11. CRISP/py.typed +1 -0
  12. CRISP/simulation_utility/__init__.py +31 -0
  13. CRISP/simulation_utility/atomic_indices.py +155 -0
  14. CRISP/simulation_utility/atomic_traj_linemap.py +278 -0
  15. CRISP/simulation_utility/error_analysis.py +254 -0
  16. CRISP/simulation_utility/interatomic_distances.py +200 -0
  17. CRISP/simulation_utility/subsampling.py +241 -0
  18. CRISP/tests/DataAnalysis/__init__.py +1 -0
  19. CRISP/tests/DataAnalysis/test_clustering_extended.py +212 -0
  20. CRISP/tests/DataAnalysis/test_contact_coordination.py +184 -0
  21. CRISP/tests/DataAnalysis/test_contact_coordination_extended.py +465 -0
  22. CRISP/tests/DataAnalysis/test_h_bond_complete.py +326 -0
  23. CRISP/tests/DataAnalysis/test_h_bond_extended.py +322 -0
  24. CRISP/tests/DataAnalysis/test_msd_complete.py +305 -0
  25. CRISP/tests/DataAnalysis/test_msd_extended.py +522 -0
  26. CRISP/tests/DataAnalysis/test_prdf.py +206 -0
  27. CRISP/tests/DataAnalysis/test_volumetric_atomic_density.py +463 -0
  28. CRISP/tests/SimulationUtility/__init__.py +1 -0
  29. CRISP/tests/SimulationUtility/test_atomic_traj_linemap.py +101 -0
  30. CRISP/tests/SimulationUtility/test_atomic_traj_linemap_extended.py +469 -0
  31. CRISP/tests/SimulationUtility/test_error_analysis_extended.py +151 -0
  32. CRISP/tests/SimulationUtility/test_interatomic_distances.py +223 -0
  33. CRISP/tests/SimulationUtility/test_subsampling.py +365 -0
  34. CRISP/tests/__init__.py +1 -0
  35. CRISP/tests/test_CRISP.py +28 -0
  36. CRISP/tests/test_cli.py +87 -0
  37. CRISP/tests/test_crisp_comprehensive.py +679 -0
  38. crisp_ase-1.1.2.dist-info/METADATA +141 -0
  39. crisp_ase-1.1.2.dist-info/RECORD +42 -0
  40. crisp_ase-1.1.2.dist-info/WHEEL +5 -0
  41. crisp_ase-1.1.2.dist-info/entry_points.txt +2 -0
  42. 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."""