pyNIBS 0.2024.8__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 (107) hide show
  1. pyNIBS-0.2024.8.dist-info/LICENSE +623 -0
  2. pyNIBS-0.2024.8.dist-info/METADATA +723 -0
  3. pyNIBS-0.2024.8.dist-info/RECORD +107 -0
  4. pyNIBS-0.2024.8.dist-info/WHEEL +5 -0
  5. pyNIBS-0.2024.8.dist-info/top_level.txt +1 -0
  6. pynibs/__init__.py +34 -0
  7. pynibs/coil.py +1367 -0
  8. pynibs/congruence/__init__.py +15 -0
  9. pynibs/congruence/congruence.py +1108 -0
  10. pynibs/congruence/ext_metrics.py +257 -0
  11. pynibs/congruence/stimulation_threshold.py +318 -0
  12. pynibs/data/configuration_exp0.yaml +59 -0
  13. pynibs/data/configuration_linear_MEP.yaml +61 -0
  14. pynibs/data/configuration_linear_RT.yaml +61 -0
  15. pynibs/data/configuration_sigmoid4.yaml +68 -0
  16. pynibs/data/network mapping configuration/configuration guide.md +238 -0
  17. pynibs/data/network mapping configuration/configuration_TEMPLATE.yaml +42 -0
  18. pynibs/data/network mapping configuration/configuration_for_testing.yaml +43 -0
  19. pynibs/data/network mapping configuration/configuration_modelTMS.yaml +43 -0
  20. pynibs/data/network mapping configuration/configuration_reg_isi_05.yaml +43 -0
  21. pynibs/data/network mapping configuration/output_documentation.md +185 -0
  22. pynibs/data/network mapping configuration/recommendations_for_accuracy_threshold.md +77 -0
  23. pynibs/data/neuron/models/L23_PC_cADpyr_biphasic_v1.csv +1281 -0
  24. pynibs/data/neuron/models/L23_PC_cADpyr_monophasic_v1.csv +1281 -0
  25. pynibs/data/neuron/models/L4_LBC_biphasic_v1.csv +1281 -0
  26. pynibs/data/neuron/models/L4_LBC_monophasic_v1.csv +1281 -0
  27. pynibs/data/neuron/models/L4_NBC_biphasic_v1.csv +1281 -0
  28. pynibs/data/neuron/models/L4_NBC_monophasic_v1.csv +1281 -0
  29. pynibs/data/neuron/models/L4_SBC_biphasic_v1.csv +1281 -0
  30. pynibs/data/neuron/models/L4_SBC_monophasic_v1.csv +1281 -0
  31. pynibs/data/neuron/models/L5_TTPC2_cADpyr_biphasic_v1.csv +1281 -0
  32. pynibs/data/neuron/models/L5_TTPC2_cADpyr_monophasic_v1.csv +1281 -0
  33. pynibs/expio/Mep.py +1518 -0
  34. pynibs/expio/__init__.py +8 -0
  35. pynibs/expio/brainsight.py +979 -0
  36. pynibs/expio/brainvis.py +71 -0
  37. pynibs/expio/cobot.py +239 -0
  38. pynibs/expio/exp.py +1876 -0
  39. pynibs/expio/fit_funs.py +287 -0
  40. pynibs/expio/localite.py +1987 -0
  41. pynibs/expio/signal_ced.py +51 -0
  42. pynibs/expio/visor.py +624 -0
  43. pynibs/freesurfer.py +502 -0
  44. pynibs/hdf5_io/__init__.py +10 -0
  45. pynibs/hdf5_io/hdf5_io.py +1857 -0
  46. pynibs/hdf5_io/xdmf.py +1542 -0
  47. pynibs/mesh/__init__.py +3 -0
  48. pynibs/mesh/mesh_struct.py +1394 -0
  49. pynibs/mesh/transformations.py +866 -0
  50. pynibs/mesh/utils.py +1103 -0
  51. pynibs/models/_TMS.py +211 -0
  52. pynibs/models/__init__.py +0 -0
  53. pynibs/muap.py +392 -0
  54. pynibs/neuron/__init__.py +2 -0
  55. pynibs/neuron/neuron_regression.py +284 -0
  56. pynibs/neuron/util.py +58 -0
  57. pynibs/optimization/__init__.py +5 -0
  58. pynibs/optimization/multichannel.py +278 -0
  59. pynibs/optimization/opt_mep.py +152 -0
  60. pynibs/optimization/optimization.py +1445 -0
  61. pynibs/optimization/workhorses.py +698 -0
  62. pynibs/pckg/__init__.py +0 -0
  63. pynibs/pckg/biosig/biosig4c++-1.9.5.src_fixed.tar.gz +0 -0
  64. pynibs/pckg/libeep/__init__.py +0 -0
  65. pynibs/pckg/libeep/pyeep.so +0 -0
  66. pynibs/regression/__init__.py +11 -0
  67. pynibs/regression/dual_node_detection.py +2375 -0
  68. pynibs/regression/regression.py +2984 -0
  69. pynibs/regression/score_types.py +0 -0
  70. pynibs/roi/__init__.py +2 -0
  71. pynibs/roi/roi.py +895 -0
  72. pynibs/roi/roi_structs.py +1233 -0
  73. pynibs/subject.py +1009 -0
  74. pynibs/tensor_scaling.py +144 -0
  75. pynibs/tests/data/InstrumentMarker20200225163611937.xml +19 -0
  76. pynibs/tests/data/TriggerMarkers_Coil0_20200225163443682.xml +14 -0
  77. pynibs/tests/data/TriggerMarkers_Coil1_20200225170337572.xml +6373 -0
  78. pynibs/tests/data/Xdmf.dtd +89 -0
  79. pynibs/tests/data/brainsight_niiImage_nifticoord.txt +145 -0
  80. pynibs/tests/data/brainsight_niiImage_nifticoord_largefile.txt +1434 -0
  81. pynibs/tests/data/brainsight_niiImage_niifticoord_mixedtargets.txt +47 -0
  82. pynibs/tests/data/create_subject_testsub.py +332 -0
  83. pynibs/tests/data/data.hdf5 +0 -0
  84. pynibs/tests/data/geo.hdf5 +0 -0
  85. pynibs/tests/test_coil.py +474 -0
  86. pynibs/tests/test_elements2nodes.py +100 -0
  87. pynibs/tests/test_hdf5_io/test_xdmf.py +61 -0
  88. pynibs/tests/test_mesh_transformations.py +123 -0
  89. pynibs/tests/test_mesh_utils.py +143 -0
  90. pynibs/tests/test_nnav_imports.py +101 -0
  91. pynibs/tests/test_quality_measures.py +117 -0
  92. pynibs/tests/test_regressdata.py +289 -0
  93. pynibs/tests/test_roi.py +17 -0
  94. pynibs/tests/test_rotations.py +86 -0
  95. pynibs/tests/test_subject.py +71 -0
  96. pynibs/tests/test_util.py +24 -0
  97. pynibs/tms_pulse.py +34 -0
  98. pynibs/util/__init__.py +4 -0
  99. pynibs/util/dosing.py +233 -0
  100. pynibs/util/quality_measures.py +562 -0
  101. pynibs/util/rotations.py +340 -0
  102. pynibs/util/simnibs.py +763 -0
  103. pynibs/util/util.py +727 -0
  104. pynibs/visualization/__init__.py +2 -0
  105. pynibs/visualization/para.py +4372 -0
  106. pynibs/visualization/plot_2D.py +137 -0
  107. pynibs/visualization/render_3D.py +347 -0
@@ -0,0 +1,123 @@
1
+ import unittest
2
+ import numpy as np
3
+ import os
4
+ import meshio
5
+ import tempfile
6
+ from pynibs import (point_data_to_cell_data_vtk,
7
+ cell_data_to_point_data_vtk,
8
+ cell_data_to_point_data,
9
+ data_nodes2elements)
10
+
11
+
12
+ class TestMeshFunctions(unittest.TestCase):
13
+ def setUp(self):
14
+ # Create a simple mesh for testing
15
+ self.points = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0]])
16
+ self.con = np.array([[0, 1, 2]])
17
+ cells = [("triangle", self.con)]
18
+
19
+ self.cell_data = {"test": [np.array([[1]])]}
20
+ self.mesh_cell_data_1d = meshio.Mesh(self.points, cells, cell_data=self.cell_data)
21
+
22
+ self.cell_data = {"test": [np.array([[1, 2, 3]])]}
23
+ self.mesh_cell_data_3d = meshio.Mesh(self.points, cells, cell_data=self.cell_data)
24
+
25
+ cell_data = {"test": [np.array([[1, 2, 3]])],
26
+ "test2": [np.array([[100, 200, 300]])]}
27
+ self.mesh_cell_datas_3d = meshio.Mesh(self.points, cells, cell_data=cell_data)
28
+
29
+ # Create another mesh for testing with point data
30
+ self.point_data = {"test": np.array([[1, 2, 3],
31
+ [1, 2, 3],
32
+ [1, 2, 3]])}
33
+ self.mesh_point_data_3d = meshio.Mesh(self.points, cells, point_data=self.point_data)
34
+
35
+ point_data = {"test": np.array([1, 2, 3])}
36
+ self.mesh_point_data_1d = meshio.Mesh(self.points, cells, point_data=point_data)
37
+
38
+ point_data = {"test": np.array([1, 2, 3]),
39
+ "test2": np.array([100, 200, 300])}
40
+ self.mesh_point_datas_1d = meshio.Mesh(self.points, cells, point_data=point_data)
41
+
42
+ def test_point_to_cell_data(self):
43
+ # Test conversion from point data to cell data for both meshes
44
+ for mesh in [self.mesh_point_data_1d, self.mesh_point_data_3d]:
45
+ with tempfile.NamedTemporaryFile(suffix=".vtu") as temp_file:
46
+ cell_data = point_data_to_cell_data_vtk(mesh, fn=temp_file.name)
47
+ self.assertTrue("test" in cell_data)
48
+ self.assertEqual(cell_data["test"].shape[0], mesh.cells[0].data.shape[0])
49
+ self.assertTrue(os.path.exists(temp_file.name))
50
+
51
+ def test_cell_to_point_data(self):
52
+ # Test conversion from cell data to point data for both meshes
53
+ for mesh in [self.mesh_cell_data_1d, self.mesh_cell_data_3d]:
54
+ with tempfile.NamedTemporaryFile(suffix=".vtu") as temp_file:
55
+ point_data = cell_data_to_point_data_vtk(mesh, fn=temp_file.name)
56
+ self.assertTrue("test" in point_data)
57
+ self.assertEqual(point_data["test"].shape[0], mesh.points.shape[0])
58
+ self.assertTrue(os.path.exists(temp_file.name))
59
+
60
+ def test_point_to_cell_data_from_con(self):
61
+ # Test conversion from point data to cell data for both meshes
62
+ for mesh in [self.mesh_point_data_1d, self.mesh_point_data_3d]:
63
+ with tempfile.NamedTemporaryFile(suffix=".vtu") as temp_file:
64
+ cell_data = point_data_to_cell_data_vtk(nodes=self.points,
65
+ con=self.con,
66
+ point_data=self.point_data, fn=temp_file.name)
67
+ self.assertTrue("test" in cell_data)
68
+ self.assertEqual(cell_data["test"].shape[0], mesh.cells[0].data.shape[0])
69
+ self.assertTrue(os.path.exists(temp_file.name))
70
+
71
+ def test_cell_to_point_data_from_con(self):
72
+ # Test conversion from cell data to point data for both meshes
73
+ for mesh in [self.mesh_cell_data_1d, self.mesh_cell_data_3d]:
74
+ with tempfile.NamedTemporaryFile(suffix=".vtu") as temp_file:
75
+ point_data = cell_data_to_point_data_vtk(nodes=self.points,
76
+ con=self.con,
77
+ cell_data=self.cell_data,
78
+ fn=temp_file.name)
79
+ self.assertTrue("test" in point_data)
80
+ self.assertEqual(point_data["test"].shape[0], mesh.points.shape[0])
81
+ self.assertTrue(os.path.exists(temp_file.name))
82
+
83
+ def test_point_to_cell_multiple_data(self):
84
+ # Test conversion from point data to cell data for both meshes
85
+ mesh = self.mesh_point_datas_1d
86
+ with tempfile.NamedTemporaryFile(suffix=".vtu") as temp_file:
87
+ cell_data = point_data_to_cell_data_vtk(mesh, fn=temp_file.name)
88
+ self.assertTrue("test" in cell_data)
89
+ self.assertEqual(cell_data["test"].shape[0], mesh.cells[0].data.shape[0])
90
+ self.assertTrue("test2" in cell_data)
91
+ self.assertEqual(cell_data["test2"].shape[0], mesh.cells[0].data.shape[0])
92
+ self.assertTrue(os.path.exists(temp_file.name))
93
+
94
+ def test_cell_to_point_multiple_data(self):
95
+ # Test conversion from cell data to point data for both meshes
96
+ mesh = self.mesh_cell_datas_3d
97
+ with tempfile.NamedTemporaryFile(suffix=".vtu") as temp_file:
98
+ point_data = cell_data_to_point_data_vtk(mesh, fn=temp_file.name)
99
+ self.assertTrue("test" in point_data)
100
+ self.assertEqual(point_data["test"].shape[0], mesh.points.shape[0])
101
+ self.assertTrue("test2" in point_data)
102
+ self.assertEqual(point_data["test2"].shape[0], mesh.points.shape[0])
103
+ self.assertTrue(os.path.exists(temp_file.name))
104
+
105
+ def test_cell_data_to_point_data(self):
106
+ # Convert the cell data to point data
107
+ data_nodes = cell_data_to_point_data(self.con, self.cell_data['test'][0], self.points)
108
+
109
+ # Check the result
110
+ self.assertEqual(data_nodes.shape, self.points.shape)
111
+ self.assertTrue(np.all(data_nodes >= 1) and np.all(data_nodes <= 4))
112
+
113
+ def test_data_nodes2elements(self):
114
+ # Convert the node data to element data
115
+ data_tris = data_nodes2elements(self.point_data['test'], self.con)
116
+
117
+ # Check the result
118
+ self.assertEqual(data_tris.shape, self.con.shape)
119
+ self.assertTrue(np.all(data_tris >= 1) and np.all(data_tris <= 4))
120
+
121
+
122
+ if __name__ == "__main__":
123
+ unittest.main()
@@ -0,0 +1,143 @@
1
+ import os
2
+ import pynibs
3
+ import unittest
4
+ import numpy as np
5
+
6
+
7
+ class TestFindNearest(unittest.TestCase):
8
+ monot_arr = np.array([0, 1, 2, 3, 5, 10])
9
+ nonmonot_arr = np.array([10, 0, 1, 2, 3, 5, 10])
10
+ value = 5
11
+
12
+ def test_01_find_nearest(self):
13
+ idx = pynibs.find_nearest(array=self.monot_arr, value=self.value)
14
+ assert idx == 4
15
+
16
+ def test_02_find_nearest_fail(self):
17
+ idx = pynibs.find_nearest(array=self.nonmonot_arr, value=self.value)
18
+ assert idx == -1
19
+
20
+
21
+ class TestCalcSampleSphere(unittest.TestCase):
22
+ mesh_hdf5_fn = os.path.join(pynibs.__testdatadir__, 'geo.hdf5')
23
+ mesh = pynibs.load_mesh_hdf5(mesh_hdf5_fn)
24
+
25
+ def test_01_sample_sphere_even(self):
26
+ with self.assertRaises(AssertionError):
27
+ pynibs.sample_sphere(n_points=10, r=10)
28
+
29
+ def test_02_sample_sphere_odd(self):
30
+ points = pynibs.sample_sphere(n_points=11, r=10)
31
+ assert points.shape == (11, 3)
32
+
33
+
34
+ class TestCalcTriSurface(unittest.TestCase):
35
+ mesh_hdf5_fn = os.path.join(pynibs.__testdatadir__, 'geo.hdf5')
36
+ mesh = pynibs.load_mesh_hdf5(mesh_hdf5_fn)
37
+
38
+ def test_01_calc_tri_volume(self):
39
+ tri_surf = pynibs.calc_tri_surface(points=self.mesh.points[self.mesh.triangles])
40
+ assert tri_surf.size == self.mesh.triangles.shape[0]
41
+ assert (tri_surf > 0).all()
42
+
43
+
44
+ class TestCalcTetVolume(unittest.TestCase):
45
+ mesh_hdf5_fn = os.path.join(pynibs.__testdatadir__, 'geo.hdf5')
46
+ mesh = pynibs.load_mesh_hdf5(mesh_hdf5_fn)
47
+
48
+ def test_01_calc_tet_volume_abs(self):
49
+ tet_vol = pynibs.calc_tet_volume(points=self.mesh.points[self.mesh.tetrahedra])
50
+ assert tet_vol.size == self.mesh.tetrahedra.shape[0]
51
+ assert (tet_vol > 0).all()
52
+
53
+ def test_02_calc_tet_volume_no_abs(self):
54
+ tet_vol = pynibs.calc_tet_volume(points=self.mesh.points[self.mesh.tetrahedra],
55
+ abs=False)
56
+ assert tet_vol.size == self.mesh.tetrahedra.shape[0]
57
+ assert (tet_vol < 0).any()
58
+
59
+
60
+ class TestGetSphere(unittest.TestCase):
61
+ """
62
+ Test pynibs.mesh.utils.* functions.
63
+ """
64
+ mesh_hdf5_fn = os.path.join(pynibs.__testdatadir__, 'geo.hdf5')
65
+ target_empty = [10, 20, 30]
66
+ target_full_tris = [0., 45.2227265, 71.97155693]
67
+ target_full_tets = [9.51698047, 84.46553784, 0.]
68
+ radius = 3
69
+
70
+ def test_01_get_sphere_fails(self):
71
+ # no mesh and no mesh_fn
72
+ with self.assertRaises(AssertionError):
73
+ pynibs.get_sphere(mesh=None,
74
+ mesh_fn=None,
75
+ target=self.target_empty,
76
+ radius=self.radius)
77
+
78
+ # no target
79
+ with self.assertRaises(AssertionError):
80
+ pynibs.get_sphere(mesh=None, mesh_fn=self.mesh_hdf5_fn)
81
+
82
+ # wrong element type
83
+ with self.assertRaises(ValueError):
84
+ # no radius, return nearest element
85
+ pynibs.get_sphere(mesh=None,
86
+ mesh_fn=self.mesh_hdf5_fn,
87
+ target=self.target_empty,
88
+ elmtype='square')
89
+
90
+ def test_02_get_sphere_nearest_tri(self):
91
+ # no radius, return nearest element
92
+ nearest_elm = pynibs.get_sphere(mesh=None,
93
+ mesh_fn=self.mesh_hdf5_fn,
94
+ target=self.target_empty)
95
+ assert nearest_elm == 681
96
+
97
+ nearest_elm = pynibs.get_sphere(mesh=None,
98
+ mesh_fn=self.mesh_hdf5_fn,
99
+ target=self.target_empty,
100
+ elmtype='Triangle')
101
+ assert nearest_elm == 681
102
+
103
+ def test_02_get_sphere_nearest_tet(self):
104
+ # no radius, return nearest element
105
+ nearest_elm = pynibs.get_sphere(mesh=None,
106
+ mesh_fn=self.mesh_hdf5_fn,
107
+ target=self.target_empty,
108
+ elmtype='tets')
109
+ assert nearest_elm == 20232
110
+
111
+ nearest_elm = pynibs.get_sphere(mesh=None,
112
+ mesh_fn=self.mesh_hdf5_fn,
113
+ target=self.target_empty,
114
+ elmtype='Tetrahedron')
115
+ assert nearest_elm == 20232
116
+
117
+ # return sphere, but should be empty
118
+ sphere_empty = pynibs.get_sphere(mesh=None,
119
+ mesh_fn=self.mesh_hdf5_fn,
120
+ target=self.target_empty,
121
+ radius=5)
122
+ assert sphere_empty.size == 0
123
+
124
+ def test_02_get_sphere_tris(self):
125
+ # return tri sphere, should not be empty
126
+ sphere_full_tris = pynibs.get_sphere(mesh=None,
127
+ mesh_fn=self.mesh_hdf5_fn,
128
+ target=self.target_full_tris,
129
+ radius=10)
130
+ assert sphere_full_tris.size == 13
131
+
132
+ def test_03_get_sphere_tets(self):
133
+ # return tet sphere, should not be empty
134
+ sphere_full_tets = pynibs.get_sphere(mesh=None,
135
+ mesh_fn=self.mesh_hdf5_fn,
136
+ target=self.target_full_tets,
137
+ radius=10,
138
+ elmtype='tets')
139
+ assert sphere_full_tets.size == 50
140
+
141
+
142
+ if __name__ == '__main__':
143
+ unittest.main()
@@ -0,0 +1,101 @@
1
+ import unittest
2
+ import numpy as np
3
+ import pynibs
4
+
5
+ class TestPyNIBSLocalite(unittest.TestCase):
6
+ def setUp(self):
7
+ # Set up any necessary paths to your .xml files here
8
+ self.valid_im_path = f"{pynibs.__testdatadir__}/InstrumentMarker20200225163611937.xml"
9
+ self.valid_tm_path = f"{pynibs.__testdatadir__}/TriggerMarkers_Coil1_20200225170337572.xml"
10
+ self.im_test = np.array([[[ 0.9013614 , 0.29839201, -0.3138959 , -71.18429093],
11
+ [ 0.38530408, -0.8834528 , 0.26659466, -79.95375675],
12
+ [ -0.19776044, -0.36123975, -0.91127244, 38.69342634],
13
+ [ 0. , 0. , 0. , 1. ]]])
14
+ self.self_tm_0 = np.array([[ 0.89563849, 0.37653939, -0.2367116 , -71.16214655],
15
+ [ 0.42635749, -0.87839562, 0.21592364, -75.74648256],
16
+ [ -0.12662378, -0.29431584, -0.94727356, 38.65814855],
17
+ [ 0. , 0. , 0. , 1. ]])
18
+
19
+ # def test_get_instrument_marker_valid(self):
20
+ # # Test get_instrument_marker with valid input
21
+ # final_arr, marker_description = pynibs.localite.get_instrument_marker(self.valid_im_path)
22
+ # # Assert expected result here
23
+ # assert np.isclose(final_arr, self.im_test).all()
24
+ # assert np.array(marker_description) == np.array(['test'])
25
+ #
26
+ # def test_get_instrument_marker_list(self):
27
+ # # Test get_instrument_marker with a list of valid paths
28
+ # final_arr, marker_description = pynibs.localite.get_instrument_marker([self.valid_im_path, self.valid_im_path])
29
+ # # Assert expected result here
30
+ # assert len(final_arr) == len(marker_description) == 2
31
+ # assert np.isclose(final_arr[0], self.im_test).all()
32
+ # assert np.isclose(final_arr[1], self.im_test).all()
33
+ # assert np.array(marker_description[0]) == np.array(['test'])
34
+ # assert np.array(marker_description[1]) == np.array(['test'])
35
+
36
+ def test_get_marker_valid(self):
37
+ # Test get_marker with valid parameters
38
+ final_arr, marker_description, times = pynibs.localite.get_marker(self.valid_im_path, 'InstrumentMarker')
39
+ # Assert expected result here
40
+ assert np.isclose(final_arr, self.im_test).all()
41
+ assert np.array(marker_description) == np.array(['test'])
42
+ assert times == []
43
+
44
+ def test_read_triggermarker_localite_valid(self):
45
+ # Test read_triggermarker_localite with valid input
46
+ m_nnav, didt, mso, descr, rec_time = pynibs.localite.read_triggermarker_localite(self.valid_tm_path)
47
+
48
+ assert m_nnav.shape == (4,4,400)
49
+ assert np.isclose(self.self_tm_0, m_nnav[:,:,0]).all()
50
+
51
+ assert len(didt) == 400
52
+ assert (didt[:2] == np.array([1,4])).all()
53
+
54
+ assert len(mso) == 400
55
+ assert (mso[:2] == np.array([3, 6])).all()
56
+
57
+ assert len(descr) == 400
58
+
59
+ assert len(rec_time) == 400
60
+ assert rec_time[:3] == ['475056', '475251', '475450']
61
+
62
+
63
+ class TestReadTargetsBrainsight(unittest.TestCase):
64
+ def setUp(self):
65
+ # Prepare a valid test file
66
+ self.valid_file = f"{pynibs.__testdatadir__}/brainsight_niiImage_nifticoord.txt"
67
+ self.target0 = np.array([[-9.9700e-01, 5.3000e-02, 5.9000e-02, 1.5020e+01],
68
+ [-5.6000e-02, -9.9700e-01, -4.4000e-02, 5.3062e+01],
69
+ [ 5.6000e-02, -4.8000e-02, 9.9700e-01, 8.7219e+01],
70
+ [ 0.0000e+00, 0.0000e+00, 0.0000e+00, 1.0000e+00]])
71
+
72
+ self.sample0 = np.array([[ 7.1200e-01, 3.2100e-01, -6.2400e-01, -4.4198e+01],
73
+ [ 1.3000e-02, 8.8300e-01, 4.6900e-01, 7.5810e+01],
74
+ [ 7.0200e-01, -3.4200e-01, 6.2400e-01, 6.5742e+01],
75
+ [ 0.0000e+00, 0.0000e+00, 0.0000e+00, 1.0000e+00]])
76
+
77
+ def test_read_targets_brainsight(self):
78
+ # Test reading from a valid file
79
+ result = pynibs.brainsight.read_targets_brainsight(self.valid_file)
80
+ assert len(result) == 4
81
+ assert np.isclose(self.target0, result[:,:,0]).all()
82
+ self.assertEqual(result.shape, (4, 4, 7)) # Expecting one target with a 4x4 matrix
83
+
84
+
85
+ def test_get_marker_targets(self):
86
+ # Test reading from a valid file
87
+ matsimnibs, description, timings = pynibs.brainsight.get_marker(self.valid_file, markertype='Targets')
88
+ assert matsimnibs.shape == (7,4,4)
89
+ assert np.isclose(self.target0, matsimnibs[0,:,:]).all()
90
+
91
+
92
+ def test_get_marker_samples(self):
93
+ # Test reading from a valid file
94
+ matsimnibs, description, timings = pynibs.brainsight.get_marker(self.valid_file, markertype='Samples')
95
+ assert matsimnibs.shape == (101,4,4)
96
+ assert matsimnibs.shape[0] == len(description) == len(timings)
97
+ assert np.isclose(self.sample0, matsimnibs[0,:,:]).all()
98
+
99
+
100
+ if __name__ == '__main__':
101
+ unittest.main()
@@ -0,0 +1,117 @@
1
+ import gdist
2
+ import pynibs
3
+ import unittest
4
+ import numpy as np
5
+
6
+
7
+ class TestGeodesicDist(unittest.TestCase):
8
+ """
9
+ Test pynibs.quality_measures.geodesic_dist()
10
+ """
11
+ nodes1 = np.array([[0, 0, 0], [1, 1, 1], [2, 2, 2]])
12
+ tris1 = np.array([[0, 1, 2]])
13
+ nodes2 = np.array([[0, 0, 0], [1, 1, 1], [5, 2, 0], [5, 3, 5]])
14
+ tris2 = np.array([[0, 1, 2], [1, 2, 3]])
15
+ source_node = np.array([0])
16
+ source_tri = 1
17
+ source_is_node_node = True
18
+ source_is_node_tri = False
19
+
20
+ def test_geodesic_dist_node(self):
21
+ # Test case 1: source is a node
22
+ nodes_dist, tris_dist = pynibs.geodesic_dist(self.nodes1, self.tris1, self.source_node,
23
+ self.source_is_node_node)
24
+
25
+ assert isinstance(nodes_dist, np.ndarray)
26
+ assert isinstance(tris_dist, np.ndarray)
27
+ assert np.allclose(nodes_dist, [0, np.sqrt(3), 2 * np.sqrt(3)])
28
+ assert np.allclose(tris_dist, [np.sqrt(3)])
29
+
30
+ '''
31
+ # doesn't work yet:
32
+ def test_geodesic_dist_tri(self):
33
+ # Test case 2: source is a triangle
34
+ nodes_dist, tris_dist = pynibs.geodesic_dist(self.nodes2, self.tris2, self.source_tri, self.source_is_node_tri)
35
+
36
+ assert isinstance(nodes_dist, np.ndarray)
37
+ assert isinstance(tris_dist, np.ndarray)
38
+ assert np.allclose(nodes_dist, ?)
39
+ assert np.allclose(tris_dist, ?)
40
+ '''
41
+
42
+
43
+ class TestEuclideanDist(unittest.TestCase):
44
+ """
45
+ Test pynibs.quality_measures.euclidean_dist()
46
+ """
47
+ nodes = np.array([[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3]])
48
+ tris = np.array([[0, 1, 2], [1, 2, 3]])
49
+ source_node = np.array([0])
50
+ source_tri = np.array([0])
51
+ source_is_node_node = True
52
+ source_is_node_tri = False
53
+
54
+ def test_euclidean_dist_node(self):
55
+ # Test case 1: source is a node
56
+ nodes_dist, tris_dist = pynibs.euclidean_dist(self.nodes[:-1], self.tris[:-1], self.source_node,
57
+ self.source_is_node_node)
58
+
59
+ assert isinstance(nodes_dist, np.ndarray)
60
+ assert isinstance(tris_dist, np.ndarray)
61
+ assert np.allclose(nodes_dist, [0, np.sqrt(3), 2 * np.sqrt(3)])
62
+ assert np.allclose(tris_dist, [np.sqrt(3)])
63
+
64
+ '''
65
+ doesnt work yet:
66
+ def test_euclidean_dist_tri(self):
67
+ # Test case 2: source is a triangle
68
+ nodes_dist, tris_dist = pynibs.euclidean_dist(self.nodes, self.tris, self.source_tri, self.source_is_node_tri)
69
+
70
+ assert isinstance(nodes_dist, np.ndarray)
71
+ assert isinstance(tris_dist, np.ndarray)
72
+ assert np.allclose(nodes_dist, [0, np.sqrt(3), 2 * np.sqrt(3), 3 * np.sqrt(3)])
73
+ assert np.allclose(tris_dist, [0, np.sqrt(3)])
74
+ '''
75
+
76
+
77
+ class TestIntersectionVecPlan(unittest.TestCase):
78
+
79
+ def test_direct_intersection(self):
80
+ ray_dir = np.array([0, 0, -1])
81
+ ray_origin = np.array([0, 0, 10])
82
+ plane_n = np.array([0, 0, 1])
83
+ plane_p = np.array([0, 0, 0])
84
+ expected_intersection = np.array([0, 0, 0])
85
+ intersec = pynibs.intersection_vec_plan(ray_dir, ray_origin, plane_n, plane_p)
86
+ np.testing.assert_array_almost_equal(intersec, expected_intersection)
87
+
88
+ def test_parallel_no_intersection(self):
89
+ ray_dir = np.array([0, 1, 0])
90
+ ray_origin = np.array([0, 0, 10])
91
+ plane_n = np.array([0, 0, 1])
92
+ plane_p = np.array([0, 0, 0])
93
+ intersec = pynibs.intersection_vec_plan(ray_dir, ray_origin, plane_n, plane_p)
94
+ assert (intersec == [np.inf, np.inf, np.inf]).all()
95
+
96
+ def test_origin_within_plane(self):
97
+ ray_dir = np.array([0, 0, 1])
98
+ ray_origin = np.array([0, 0, 0])
99
+ plane_n = np.array([0, 0, 1])
100
+ plane_p = np.array([0, 0, 0])
101
+ expected_intersection = np.array([0, 0, 0])
102
+ intersec = pynibs.intersection_vec_plan(ray_dir, ray_origin, plane_n, plane_p)
103
+ np.testing.assert_array_almost_equal(intersec, expected_intersection)
104
+
105
+ def test_edge_case_near_parallel(self):
106
+ ray_dir = np.array([0.001, 0.001, -1])
107
+ ray_origin = np.array([0, 0, 10])
108
+ plane_n = np.array([0, 0, 1])
109
+ plane_p = np.array([0, 0, 0])
110
+ # Expected intersection is approximate due to near parallel direction
111
+ expected_intersection = np.array([0.01, 0.01, 0])
112
+ intersec = pynibs.intersection_vec_plan(ray_dir, ray_origin, plane_n, plane_p)
113
+ np.testing.assert_array_almost_equal(intersec, expected_intersection, decimal=2)
114
+
115
+
116
+ if __name__ == '__main__':
117
+ unittest.main()