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,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