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.
- pyNIBS-0.2024.8.dist-info/LICENSE +623 -0
- pyNIBS-0.2024.8.dist-info/METADATA +723 -0
- pyNIBS-0.2024.8.dist-info/RECORD +107 -0
- pyNIBS-0.2024.8.dist-info/WHEEL +5 -0
- pyNIBS-0.2024.8.dist-info/top_level.txt +1 -0
- pynibs/__init__.py +34 -0
- pynibs/coil.py +1367 -0
- pynibs/congruence/__init__.py +15 -0
- pynibs/congruence/congruence.py +1108 -0
- pynibs/congruence/ext_metrics.py +257 -0
- pynibs/congruence/stimulation_threshold.py +318 -0
- pynibs/data/configuration_exp0.yaml +59 -0
- pynibs/data/configuration_linear_MEP.yaml +61 -0
- pynibs/data/configuration_linear_RT.yaml +61 -0
- pynibs/data/configuration_sigmoid4.yaml +68 -0
- pynibs/data/network mapping configuration/configuration guide.md +238 -0
- pynibs/data/network mapping configuration/configuration_TEMPLATE.yaml +42 -0
- pynibs/data/network mapping configuration/configuration_for_testing.yaml +43 -0
- pynibs/data/network mapping configuration/configuration_modelTMS.yaml +43 -0
- pynibs/data/network mapping configuration/configuration_reg_isi_05.yaml +43 -0
- pynibs/data/network mapping configuration/output_documentation.md +185 -0
- pynibs/data/network mapping configuration/recommendations_for_accuracy_threshold.md +77 -0
- pynibs/data/neuron/models/L23_PC_cADpyr_biphasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L23_PC_cADpyr_monophasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L4_LBC_biphasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L4_LBC_monophasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L4_NBC_biphasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L4_NBC_monophasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L4_SBC_biphasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L4_SBC_monophasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L5_TTPC2_cADpyr_biphasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L5_TTPC2_cADpyr_monophasic_v1.csv +1281 -0
- pynibs/expio/Mep.py +1518 -0
- pynibs/expio/__init__.py +8 -0
- pynibs/expio/brainsight.py +979 -0
- pynibs/expio/brainvis.py +71 -0
- pynibs/expio/cobot.py +239 -0
- pynibs/expio/exp.py +1876 -0
- pynibs/expio/fit_funs.py +287 -0
- pynibs/expio/localite.py +1987 -0
- pynibs/expio/signal_ced.py +51 -0
- pynibs/expio/visor.py +624 -0
- pynibs/freesurfer.py +502 -0
- pynibs/hdf5_io/__init__.py +10 -0
- pynibs/hdf5_io/hdf5_io.py +1857 -0
- pynibs/hdf5_io/xdmf.py +1542 -0
- pynibs/mesh/__init__.py +3 -0
- pynibs/mesh/mesh_struct.py +1394 -0
- pynibs/mesh/transformations.py +866 -0
- pynibs/mesh/utils.py +1103 -0
- pynibs/models/_TMS.py +211 -0
- pynibs/models/__init__.py +0 -0
- pynibs/muap.py +392 -0
- pynibs/neuron/__init__.py +2 -0
- pynibs/neuron/neuron_regression.py +284 -0
- pynibs/neuron/util.py +58 -0
- pynibs/optimization/__init__.py +5 -0
- pynibs/optimization/multichannel.py +278 -0
- pynibs/optimization/opt_mep.py +152 -0
- pynibs/optimization/optimization.py +1445 -0
- pynibs/optimization/workhorses.py +698 -0
- pynibs/pckg/__init__.py +0 -0
- pynibs/pckg/biosig/biosig4c++-1.9.5.src_fixed.tar.gz +0 -0
- pynibs/pckg/libeep/__init__.py +0 -0
- pynibs/pckg/libeep/pyeep.so +0 -0
- pynibs/regression/__init__.py +11 -0
- pynibs/regression/dual_node_detection.py +2375 -0
- pynibs/regression/regression.py +2984 -0
- pynibs/regression/score_types.py +0 -0
- pynibs/roi/__init__.py +2 -0
- pynibs/roi/roi.py +895 -0
- pynibs/roi/roi_structs.py +1233 -0
- pynibs/subject.py +1009 -0
- pynibs/tensor_scaling.py +144 -0
- pynibs/tests/data/InstrumentMarker20200225163611937.xml +19 -0
- pynibs/tests/data/TriggerMarkers_Coil0_20200225163443682.xml +14 -0
- pynibs/tests/data/TriggerMarkers_Coil1_20200225170337572.xml +6373 -0
- pynibs/tests/data/Xdmf.dtd +89 -0
- pynibs/tests/data/brainsight_niiImage_nifticoord.txt +145 -0
- pynibs/tests/data/brainsight_niiImage_nifticoord_largefile.txt +1434 -0
- pynibs/tests/data/brainsight_niiImage_niifticoord_mixedtargets.txt +47 -0
- pynibs/tests/data/create_subject_testsub.py +332 -0
- pynibs/tests/data/data.hdf5 +0 -0
- pynibs/tests/data/geo.hdf5 +0 -0
- pynibs/tests/test_coil.py +474 -0
- pynibs/tests/test_elements2nodes.py +100 -0
- pynibs/tests/test_hdf5_io/test_xdmf.py +61 -0
- pynibs/tests/test_mesh_transformations.py +123 -0
- pynibs/tests/test_mesh_utils.py +143 -0
- pynibs/tests/test_nnav_imports.py +101 -0
- pynibs/tests/test_quality_measures.py +117 -0
- pynibs/tests/test_regressdata.py +289 -0
- pynibs/tests/test_roi.py +17 -0
- pynibs/tests/test_rotations.py +86 -0
- pynibs/tests/test_subject.py +71 -0
- pynibs/tests/test_util.py +24 -0
- pynibs/tms_pulse.py +34 -0
- pynibs/util/__init__.py +4 -0
- pynibs/util/dosing.py +233 -0
- pynibs/util/quality_measures.py +562 -0
- pynibs/util/rotations.py +340 -0
- pynibs/util/simnibs.py +763 -0
- pynibs/util/util.py +727 -0
- pynibs/visualization/__init__.py +2 -0
- pynibs/visualization/para.py +4372 -0
- pynibs/visualization/plot_2D.py +137 -0
- pynibs/visualization/render_3D.py +347 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pynibs
|
|
3
|
+
import numpy as np
|
|
4
|
+
import unittest
|
|
5
|
+
import yaml
|
|
6
|
+
from inspect import getmembers, isfunction, getfullargspec
|
|
7
|
+
|
|
8
|
+
import pynibs
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestRegressData(unittest.TestCase):
|
|
12
|
+
"""
|
|
13
|
+
Test pynibs.regress_data()
|
|
14
|
+
"""
|
|
15
|
+
np.random.seed(1)
|
|
16
|
+
|
|
17
|
+
n_elm_small = 10
|
|
18
|
+
# n_elm_large = 100000
|
|
19
|
+
n_mep_small = 10
|
|
20
|
+
# n_mep_large = 100
|
|
21
|
+
n_signed = 25 # because function run_select_signed_data needs > 20 data points
|
|
22
|
+
|
|
23
|
+
e_small = np.zeros((n_mep_small, n_elm_small))
|
|
24
|
+
e_small[:] = 1
|
|
25
|
+
e_small *= np.linspace(0.1, 5, n_mep_small)[:, np.newaxis]
|
|
26
|
+
e_small *= np.linspace(0.1, 5, n_elm_small)
|
|
27
|
+
|
|
28
|
+
e_signed = np.zeros((n_signed, n_signed))
|
|
29
|
+
e_signed[:] = 1
|
|
30
|
+
e_signed[0] = -1
|
|
31
|
+
e_signed *= np.linspace(0.1, 5, n_signed)[:, np.newaxis]
|
|
32
|
+
e_signed *= np.linspace(0.1, 5, n_signed)
|
|
33
|
+
|
|
34
|
+
mep_small = np.linspace(0.1, 5, n_mep_small)
|
|
35
|
+
|
|
36
|
+
mep_signed = np.linspace(0.1, 5, n_signed)
|
|
37
|
+
|
|
38
|
+
con_small = np.array([[i, i + 1, i + 2] for i in range(n_elm_small)])
|
|
39
|
+
|
|
40
|
+
def test_raise_assert_if_no_con(self):
|
|
41
|
+
"""
|
|
42
|
+
When refit_discontinuities=True, con cannot be None
|
|
43
|
+
"""
|
|
44
|
+
with self.assertRaises(AssertionError):
|
|
45
|
+
pynibs.regress_data(self.e_small, self.mep_small)
|
|
46
|
+
|
|
47
|
+
def test_all_default(self):
|
|
48
|
+
r2 = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False)
|
|
49
|
+
assert r2.max() > .9
|
|
50
|
+
assert r2.shape == (self.n_elm_small,)
|
|
51
|
+
|
|
52
|
+
def test_return_fits(self):
|
|
53
|
+
r2, fits = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
54
|
+
return_fits=True)
|
|
55
|
+
assert r2.max() > .9
|
|
56
|
+
assert r2.shape == (self.n_elm_small,)
|
|
57
|
+
assert len(fits) == self.n_elm_small
|
|
58
|
+
assert type(fits[0]) == dict
|
|
59
|
+
for k in ['x0', 'r', 'amp', 'y0']:
|
|
60
|
+
assert k in fits[0]
|
|
61
|
+
|
|
62
|
+
def test_elm_idx_list(self):
|
|
63
|
+
elm_idx_list = list(range(0, int(self.n_elm_small / 2)))
|
|
64
|
+
r2 = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
65
|
+
elm_idx_list=elm_idx_list)
|
|
66
|
+
assert r2.max() > .9
|
|
67
|
+
assert r2.shape == (len(elm_idx_list),)
|
|
68
|
+
|
|
69
|
+
def test_zap_idx(self):
|
|
70
|
+
zap_idx = range(int(self.n_mep_small / 2), self.n_mep_small)
|
|
71
|
+
r2 = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
72
|
+
zap_idx=zap_idx)
|
|
73
|
+
assert r2.max() > .9
|
|
74
|
+
assert r2.shape == (self.n_elm_small,)
|
|
75
|
+
|
|
76
|
+
def test_element_list_linear(self):
|
|
77
|
+
"""
|
|
78
|
+
Gives regress_data() an element_list AND fun=linear
|
|
79
|
+
"""
|
|
80
|
+
element_list = [pynibs.Element(x=self.e_small[:, ele_id],
|
|
81
|
+
y=self.mep_small,
|
|
82
|
+
ele_id=ele_id,
|
|
83
|
+
fun=pynibs.expio.fit_funs.linear,
|
|
84
|
+
score_type="R2",
|
|
85
|
+
select_signed_data=False,
|
|
86
|
+
constants=None) for ele_id in range(self.n_elm_small)]
|
|
87
|
+
r2, fits = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
88
|
+
element_list=element_list, return_fits=True)
|
|
89
|
+
assert r2.max() > .9
|
|
90
|
+
assert r2.shape == (self.n_elm_small,)
|
|
91
|
+
# let's check if really linear fits have been used
|
|
92
|
+
assert 'm' in fits[0]
|
|
93
|
+
assert 'n' in fits[0]
|
|
94
|
+
|
|
95
|
+
def test_refit_discontinuities(self):
|
|
96
|
+
r2 = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=True, n_cpu=1,
|
|
97
|
+
con=self.con_small)
|
|
98
|
+
assert r2.max() > .9
|
|
99
|
+
assert r2.shape == (self.n_elm_small,)
|
|
100
|
+
|
|
101
|
+
def test_n_refit(self):
|
|
102
|
+
r2 = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
103
|
+
n_refit=0)
|
|
104
|
+
assert r2.max() > .9
|
|
105
|
+
assert r2.shape == (self.n_elm_small,)
|
|
106
|
+
|
|
107
|
+
def test_workhorses(self): # and verbose
|
|
108
|
+
# Suppress output message in run_select_signed_data()
|
|
109
|
+
# suppress_text = io.StringIO()
|
|
110
|
+
# sys.stdout = suppress_text
|
|
111
|
+
|
|
112
|
+
r2 = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=4,
|
|
113
|
+
n_refit=0, verbose=True)
|
|
114
|
+
assert r2.max() > .9
|
|
115
|
+
assert r2.shape == (self.n_elm_small,)
|
|
116
|
+
|
|
117
|
+
# Enable output messages again
|
|
118
|
+
# sys.stdout = sys.__stdout__
|
|
119
|
+
|
|
120
|
+
def test_score_type(self):
|
|
121
|
+
sr = pynibs.regress_data(self.e_small, self.mep_small, score_type='SR', refit_discontinuities=False, n_cpu=1,
|
|
122
|
+
n_refit=0)
|
|
123
|
+
assert sr.max() > .9
|
|
124
|
+
assert sr.shape == (self.n_elm_small,)
|
|
125
|
+
|
|
126
|
+
rho = pynibs.regress_data(self.e_small, self.mep_small, score_type='rho', refit_discontinuities=False, n_cpu=1,
|
|
127
|
+
n_refit=0)
|
|
128
|
+
assert rho.max() > .9
|
|
129
|
+
assert rho.shape == (self.n_elm_small, 2)
|
|
130
|
+
|
|
131
|
+
with self.assertRaises(NotImplementedError):
|
|
132
|
+
pynibs.regress_data(self.e_small, self.mep_small, score_type='abc', refit_discontinuities=False, n_cpu=1,
|
|
133
|
+
n_refit=0)
|
|
134
|
+
|
|
135
|
+
def test_select_signed_data(self):
|
|
136
|
+
# Suppress output message in run_select_signed_data()
|
|
137
|
+
# suppress_text = io.StringIO()
|
|
138
|
+
# sys.stdout = suppress_text
|
|
139
|
+
|
|
140
|
+
# Test with mostly positive data
|
|
141
|
+
r2_pos = pynibs.regress_data(self.e_signed, self.mep_signed, select_signed_data=True,
|
|
142
|
+
refit_discontinuities=False, n_refit=0)
|
|
143
|
+
assert r2_pos.max() > 0.9
|
|
144
|
+
assert r2_pos.shape == (self.n_signed,)
|
|
145
|
+
|
|
146
|
+
# Test with mostly negative data
|
|
147
|
+
r2_neg = pynibs.regress_data(-self.e_signed, self.mep_signed, select_signed_data=True,
|
|
148
|
+
refit_discontinuities=False, n_refit=0)
|
|
149
|
+
assert r2_neg.max() > 0.9
|
|
150
|
+
assert r2_neg.shape == (self.n_signed,)
|
|
151
|
+
|
|
152
|
+
# Enable output messages again
|
|
153
|
+
# sys.stdout = sys.__stdout__
|
|
154
|
+
|
|
155
|
+
# ***** Testing all regression functions: *****
|
|
156
|
+
|
|
157
|
+
def test_dummy_fun(self):
|
|
158
|
+
r2, fits = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
159
|
+
n_refit=0, fun=pynibs.expio.fit_funs.dummy_fun, return_fits=True)
|
|
160
|
+
assert r2.shape == (self.n_elm_small,)
|
|
161
|
+
assert 'a' in fits[0]
|
|
162
|
+
|
|
163
|
+
def test_sigmoid(self):
|
|
164
|
+
r2, fits = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
165
|
+
n_refit=0, fun=pynibs.expio.fit_funs.sigmoid, return_fits=True)
|
|
166
|
+
assert r2.max() > 0.90
|
|
167
|
+
assert r2.shape == (self.n_elm_small,)
|
|
168
|
+
assert 'x0' in fits[0]
|
|
169
|
+
assert 'r' in fits[0]
|
|
170
|
+
assert 'amp' in fits[0]
|
|
171
|
+
|
|
172
|
+
def test_sigmoid_negative_values(self):
|
|
173
|
+
r2 = pynibs.regress_data(-self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
174
|
+
n_refit=0, fun=pynibs.expio.fit_funs.sigmoid)
|
|
175
|
+
assert r2.max() > 0.90
|
|
176
|
+
assert r2.shape == (self.n_elm_small,)
|
|
177
|
+
|
|
178
|
+
def test_sigmoid_log(self):
|
|
179
|
+
r2, fits = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
180
|
+
n_refit=0, fun=pynibs.expio.fit_funs.sigmoid_log, return_fits=True)
|
|
181
|
+
assert r2.max() > 0.9
|
|
182
|
+
assert r2.shape == (self.n_elm_small,)
|
|
183
|
+
assert 'x0' in fits[0]
|
|
184
|
+
assert 'r' in fits[0]
|
|
185
|
+
assert 'amp' in fits[0]
|
|
186
|
+
|
|
187
|
+
def test_sigmoid4_log(self):
|
|
188
|
+
r2, fits = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
189
|
+
n_refit=0, fun=pynibs.expio.fit_funs.sigmoid4_log, return_fits=True)
|
|
190
|
+
assert r2.max() > 0.9
|
|
191
|
+
assert r2.shape == (self.n_elm_small,)
|
|
192
|
+
assert 'x0' in fits[0]
|
|
193
|
+
assert 'r' in fits[0]
|
|
194
|
+
assert 'amp' in fits[0]
|
|
195
|
+
assert 'y0' in fits[0]
|
|
196
|
+
|
|
197
|
+
def test_exp(self):
|
|
198
|
+
r2, fits = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
199
|
+
n_refit=0, fun=pynibs.expio.fit_funs.exp0, return_fits=True)
|
|
200
|
+
assert r2.max() > 0.90
|
|
201
|
+
assert r2.shape == (self.n_elm_small,)
|
|
202
|
+
assert 'x0' in fits[0]
|
|
203
|
+
assert 'r' in fits[0]
|
|
204
|
+
|
|
205
|
+
def test_raise_assert_if_false_fun(self):
|
|
206
|
+
with self.assertRaises(NotImplementedError):
|
|
207
|
+
pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
208
|
+
n_refit=0, fun=pynibs.expio.fit_funs.linear_log)
|
|
209
|
+
|
|
210
|
+
# ***** Testing use of yaml-configuration files: *****
|
|
211
|
+
|
|
212
|
+
def test_yaml_sigmoid4(self):
|
|
213
|
+
yaml_config = os.path.join(pynibs.__datadir__, 'configuration_sigmoid4.yaml')
|
|
214
|
+
with open(yaml_config, "r") as yamlfile:
|
|
215
|
+
config = yaml.load(yamlfile, Loader=yaml.FullLoader)
|
|
216
|
+
r2 = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
217
|
+
n_refit=0, fun=pynibs.expio.fit_funs.sigmoid4, **config)
|
|
218
|
+
assert r2.max() > 0.90
|
|
219
|
+
assert r2.shape == (self.n_elm_small,)
|
|
220
|
+
|
|
221
|
+
def test_yaml_linear_MEP(self):
|
|
222
|
+
yaml_config = os.path.join(pynibs.__datadir__, 'configuration_linear_MEP.yaml')
|
|
223
|
+
with open(yaml_config, "r") as yamlfile:
|
|
224
|
+
config = yaml.load(yamlfile, Loader=yaml.FullLoader)
|
|
225
|
+
r2, fits = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
226
|
+
n_refit=0, fun=pynibs.expio.fit_funs.linear, return_fits=True, **config)
|
|
227
|
+
assert r2.max() > 0.90
|
|
228
|
+
assert r2.shape == (self.n_elm_small,)
|
|
229
|
+
assert 'm' in fits[0]
|
|
230
|
+
assert 'n' in fits[0]
|
|
231
|
+
|
|
232
|
+
def test_yaml_linear_RT(self):
|
|
233
|
+
yaml_config = os.path.join(pynibs.__datadir__, 'configuration_linear_RT.yaml')
|
|
234
|
+
with open(yaml_config, "r") as yamlfile:
|
|
235
|
+
config = yaml.load(yamlfile, Loader=yaml.FullLoader)
|
|
236
|
+
r2, fits = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
237
|
+
n_refit=0, fun=pynibs.expio.fit_funs.linear, return_fits=True, **config)
|
|
238
|
+
assert r2.max() > 0.90
|
|
239
|
+
assert r2.shape == (self.n_elm_small,)
|
|
240
|
+
assert 'm' in fits[0]
|
|
241
|
+
assert 'n' in fits[0]
|
|
242
|
+
|
|
243
|
+
def test_yaml_exp(self):
|
|
244
|
+
yaml_config = os.path.join(pynibs.__datadir__, 'configuration_exp0.yaml')
|
|
245
|
+
with open(yaml_config, "r") as yamlfile:
|
|
246
|
+
config = yaml.load(yamlfile, Loader=yaml.FullLoader)
|
|
247
|
+
r2, fits = pynibs.regress_data(self.e_small, self.mep_small, refit_discontinuities=False, n_cpu=1,
|
|
248
|
+
n_refit=0, fun=pynibs.expio.fit_funs.exp0, return_fits=True, **config)
|
|
249
|
+
assert r2.max() > 0.90
|
|
250
|
+
assert r2.shape == (self.n_elm_small,)
|
|
251
|
+
assert 'x0' in fits[0]
|
|
252
|
+
assert 'r' in fits[0]
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class TestSingleFit(unittest.TestCase):
|
|
256
|
+
|
|
257
|
+
def setUp(self):
|
|
258
|
+
self.x = x = np.array(range(1, 100))
|
|
259
|
+
self.args = {'a': 1,
|
|
260
|
+
'x0': 0,
|
|
261
|
+
'r': .2,
|
|
262
|
+
'amp': 5,
|
|
263
|
+
'y0': 0,
|
|
264
|
+
'm': 5,
|
|
265
|
+
'n': 1}
|
|
266
|
+
|
|
267
|
+
def test_single_fit(self):
|
|
268
|
+
for fun_id, fun in getmembers(pynibs.expio.fit_funs):
|
|
269
|
+
if isfunction(fun):
|
|
270
|
+
if fun_id == 'dummy_fun':
|
|
271
|
+
continue
|
|
272
|
+
|
|
273
|
+
fun_params = getfullargspec(fun)[0]
|
|
274
|
+
print(f"Testing {fun_id} with: {fun_params}")
|
|
275
|
+
fun_args = {k: self.args[k] for k in fun_params if k != 'x'}
|
|
276
|
+
y = fun(self.x, **fun_args)
|
|
277
|
+
|
|
278
|
+
fit = pynibs.single_fit(self.x, y, fun)
|
|
279
|
+
for k in fun_params:
|
|
280
|
+
if k != 'x':
|
|
281
|
+
print(
|
|
282
|
+
f"\t{k}: org: {self.args[k]} | "
|
|
283
|
+
f"fit: {fit.params[k].value} | "
|
|
284
|
+
f"{np.isclose(self.args[k], fit.params[k].value, atol=.1)}")
|
|
285
|
+
print(f"R2: {fit.rsquared}")
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
if __name__ == '__main__':
|
|
289
|
+
unittest.main()
|
pynibs/tests/test_roi.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import pynibs
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TestRegIonOfInterestSurface(unittest.TestCase):
|
|
6
|
+
|
|
7
|
+
def test_regionofinterestsurface(self):
|
|
8
|
+
roi = pynibs.RegionOfInterestSurface()
|
|
9
|
+
for field in ["X_ROI", "Y_ROI", "Z_ROI"]:
|
|
10
|
+
assert hasattr(roi, field)
|
|
11
|
+
for field in ["x_roi", "x_roi", "x_roi"]:
|
|
12
|
+
assert not hasattr(roi, field)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TestRegionOfInterestVolume(unittest.TestCase):
|
|
16
|
+
def test_regionofinterestvolume(self):
|
|
17
|
+
roi = pynibs.RegionOfInterestVolume()
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import pynibs
|
|
3
|
+
import numpy as np
|
|
4
|
+
from scipy.spatial.transform import Rotation as R
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestUtilsRotationsbases2rotmat(unittest.TestCase):
|
|
8
|
+
mat = np.array(([1., 0., 0., 0.],
|
|
9
|
+
[0., 1., 0., 0.],
|
|
10
|
+
[0., 0., 1., 0.],
|
|
11
|
+
[10., 20., 30., 1.])).T
|
|
12
|
+
|
|
13
|
+
def test_bases2rotmat(self):
|
|
14
|
+
v1 = np.array(([0, 0, 1], [0, 1, 0], [1, 0, 0]))
|
|
15
|
+
v2 = np.array(([1, 0, 0], [0, 1, 0], [0, 0, 1]))
|
|
16
|
+
res = R.from_matrix(pynibs.bases2rotmat(v1, v2)).as_euler('xyz', degrees=True)
|
|
17
|
+
assert (res == np.array([180., 0., 180.])).all()
|
|
18
|
+
|
|
19
|
+
def test_roatate_matsimnibs_euler_no_rot(self):
|
|
20
|
+
for axis in ['x', 'y', 'z']:
|
|
21
|
+
assert (self.mat == pynibs.rotate_matsimnibs_euler(axis=axis, angle=0, matsimnibs=self.mat,
|
|
22
|
+
metric='rad')).all()
|
|
23
|
+
assert (self.mat == pynibs.rotate_matsimnibs_euler(axis=axis, angle=0, matsimnibs=self.mat,
|
|
24
|
+
metric='deg')).all()
|
|
25
|
+
|
|
26
|
+
assert np.allclose(
|
|
27
|
+
pynibs.rotate_matsimnibs_euler(axis=axis, angle=2 * np.pi, matsimnibs=self.mat, metric='rad'),
|
|
28
|
+
self.mat)
|
|
29
|
+
assert np.allclose(pynibs.rotate_matsimnibs_euler(axis=axis, angle=360, matsimnibs=self.mat, metric='deg'),
|
|
30
|
+
self.mat)
|
|
31
|
+
|
|
32
|
+
def test_identity(self):
|
|
33
|
+
# Test if the function returns identity matrix for the same input
|
|
34
|
+
identity = np.eye(3)
|
|
35
|
+
result = pynibs.bases2rotmat(identity, identity)
|
|
36
|
+
np.testing.assert_array_almost_equal(result, identity)
|
|
37
|
+
|
|
38
|
+
def test_rotation(self):
|
|
39
|
+
# Test if the function correctly calculates rotation matrix
|
|
40
|
+
rot = R.from_euler('xyz', [80, 0, 0], degrees=True)
|
|
41
|
+
base = np.eye(3)
|
|
42
|
+
target = rot.as_matrix()
|
|
43
|
+
result = pynibs.bases2rotmat(target, base)
|
|
44
|
+
np.testing.assert_array_almost_equal(result, target)
|
|
45
|
+
|
|
46
|
+
def test_rotate_matsimnibs_euler_180(self):
|
|
47
|
+
assert np.allclose(pynibs.rotate_matsimnibs_euler(axis='x', angle=np.pi, matsimnibs=self.mat, metric='rad'),
|
|
48
|
+
np.array([[1., 0., 0., 10.],
|
|
49
|
+
[0., -1., -0., 20.],
|
|
50
|
+
[0., 0., -1., 30.],
|
|
51
|
+
[0., 0., 0., 1.]])
|
|
52
|
+
)
|
|
53
|
+
assert np.allclose(pynibs.rotate_matsimnibs_euler(axis='y', angle=np.pi, matsimnibs=self.mat, metric='rad'),
|
|
54
|
+
np.array([[-1., 0., 0., 10.],
|
|
55
|
+
[0., 1., 0., 20.],
|
|
56
|
+
[-0., 0., -1., 30.],
|
|
57
|
+
[0., 0., 0., 1.]]))
|
|
58
|
+
assert np.allclose(pynibs.rotate_matsimnibs_euler(axis='z', angle=np.pi, matsimnibs=self.mat, metric='rad'),
|
|
59
|
+
np.array([[-1., -0., 0., 10.],
|
|
60
|
+
[0., -1., 0., 20.],
|
|
61
|
+
[0., 0., 1., 30.],
|
|
62
|
+
[0., 0., 0., 1.]]))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class TestUtilsRotationsrotmatfromvecs(unittest.TestCase):
|
|
66
|
+
def test_rotmat_from_vecs(self):
|
|
67
|
+
vec1 = np.array([1, 0, 0])
|
|
68
|
+
vec2 = np.array([0, 1, 0])
|
|
69
|
+
expected_result = R.from_euler('z', 90, degrees=True)
|
|
70
|
+
result = pynibs.rotmat_from_vecs(vec1, vec2)
|
|
71
|
+
np.testing.assert_array_almost_equal(result.as_matrix(), expected_result.as_matrix())
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class TestUtilsRotationsrotMattoEulerAngles(unittest.TestCase):
|
|
75
|
+
def test_rotation_matrix_to_euler_angles(self):
|
|
76
|
+
# Define some Euler angles
|
|
77
|
+
euler_angles = np.array([np.pi / 2, np.pi / 3, np.pi / 4])
|
|
78
|
+
|
|
79
|
+
# Convert the Euler angles to a rotation matrix
|
|
80
|
+
rotation_matrix = pynibs.euler_angles_to_rotation_matrix(euler_angles)
|
|
81
|
+
|
|
82
|
+
# Pass the rotation matrix to the function and get the output Euler angles
|
|
83
|
+
output_euler_angles = pynibs.rotation_matrix_to_euler_angles(rotation_matrix)
|
|
84
|
+
|
|
85
|
+
# Check that the output Euler angles match the original Euler angles
|
|
86
|
+
np.testing.assert_array_almost_equal(output_euler_angles, euler_angles)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import tempfile
|
|
3
|
+
import pynibs
|
|
4
|
+
import sys
|
|
5
|
+
import shutil
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestSubject(unittest.TestCase):
|
|
10
|
+
"""
|
|
11
|
+
Test pynibs.subject.* functions.
|
|
12
|
+
"""
|
|
13
|
+
subject_id = 'testsub'
|
|
14
|
+
mesh_names = ['charm_refm1_sm', 'headreco', 'headreco_refined_m1']
|
|
15
|
+
create_subject_fn = "create_subject_testsub.py"
|
|
16
|
+
create_subject_fn_org = os.path.join(pynibs.__testdatadir__, create_subject_fn)
|
|
17
|
+
|
|
18
|
+
# get temporary folder and move create_subject.py theree
|
|
19
|
+
folder = tempfile.TemporaryDirectory()
|
|
20
|
+
fn_create_subject = os.path.join(folder.name, create_subject_fn)
|
|
21
|
+
fn_subject_object = os.path.join(folder.name, f"{subject_id}.hdf5")
|
|
22
|
+
shutil.copyfile(create_subject_fn_org, fn_create_subject)
|
|
23
|
+
|
|
24
|
+
def test_01_create_subject(self):
|
|
25
|
+
cmd = f"{sys.executable} {self.fn_create_subject}"
|
|
26
|
+
assert os.system(cmd) == 0, f"Creating subject failed"
|
|
27
|
+
|
|
28
|
+
def test_02_load_subject(self):
|
|
29
|
+
subject = pynibs.load_subject(self.fn_subject_object)
|
|
30
|
+
for mesh_id in self.mesh_names:
|
|
31
|
+
assert mesh_id in subject.mesh.keys(), f"{mesh_id} not found in {self.fn_subject_object}"
|
|
32
|
+
|
|
33
|
+
def test_03_load_subject_h5(self):
|
|
34
|
+
no_hdf5_suffix = os.path.join(self.folder.name, 'test.h5')
|
|
35
|
+
shutil.copyfile(self.fn_subject_object, no_hdf5_suffix)
|
|
36
|
+
with self.assertRaises(NotImplementedError):
|
|
37
|
+
pynibs.load_subject(no_hdf5_suffix)
|
|
38
|
+
subject = pynibs.load_subject(no_hdf5_suffix, filetype='hdf5')
|
|
39
|
+
for mesh_id in self.mesh_names:
|
|
40
|
+
assert mesh_id in subject.mesh.keys(), f"{mesh_id} not found in {self.fn_subject_object}"
|
|
41
|
+
|
|
42
|
+
def test_04_save_subject(self):
|
|
43
|
+
subject = pynibs.load_subject(self.fn_subject_object)
|
|
44
|
+
fn_new = self.fn_subject_object.replace('.hdf5', '_new.hdf5')
|
|
45
|
+
pynibs.save_subject(subject_id=self.subject_id,
|
|
46
|
+
subject_folder=self.folder.name,
|
|
47
|
+
fname=fn_new,
|
|
48
|
+
mri_dict=subject.mri,
|
|
49
|
+
mesh_dict=subject.mesh,
|
|
50
|
+
roi_dict=subject.roi,
|
|
51
|
+
exp_dict=subject.exp,
|
|
52
|
+
ps_dict=subject.ps,
|
|
53
|
+
overwrite=True,
|
|
54
|
+
check_file_exist=False,
|
|
55
|
+
verbose=False)
|
|
56
|
+
subject = pynibs.load_subject(fn_new)
|
|
57
|
+
for mesh_id in self.mesh_names:
|
|
58
|
+
assert mesh_id in subject.mesh.keys(), f"{mesh_id} not found in {self.fn_subject_object}"
|
|
59
|
+
|
|
60
|
+
# def test_06_add_stuff(self):
|
|
61
|
+
# subject = pynibs.load_subject(self.fn_subject_object)
|
|
62
|
+
# mri = subject.mri[0]
|
|
63
|
+
# subject.add_mri_info(mri)
|
|
64
|
+
#
|
|
65
|
+
# exp = {'TMS_localite': subject.exp['TMS_localite']}
|
|
66
|
+
# exp['fn_coil'] = exp['TMS_localite']['fn_coil'][0][0]
|
|
67
|
+
# subject.add_experiment_info(exp)
|
|
68
|
+
|
|
69
|
+
def test_05_print_subject(self):
|
|
70
|
+
subject = pynibs.load_subject(self.fn_subject_object)
|
|
71
|
+
print(subject)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import pynibs
|
|
2
|
+
import unittest
|
|
3
|
+
from scipy.spatial.transform import Rotation as rot
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestIntersectionVecPlan(unittest.TestCase):
|
|
8
|
+
def test_intersection(self):
|
|
9
|
+
r = rot.from_rotvec([45, 0, 0], degrees=True)
|
|
10
|
+
|
|
11
|
+
# Define plane
|
|
12
|
+
plane_n = np.array([0, 0, 1])
|
|
13
|
+
plane_p = np.array([0, 0, 20]) # Any point on the plane
|
|
14
|
+
|
|
15
|
+
# Define ray
|
|
16
|
+
ray_dir = r.apply([0, 0, 1]) # np.array([0, -1, -1])
|
|
17
|
+
ray_origin = np.array([0, 0, 0]) # Any point along the ray
|
|
18
|
+
|
|
19
|
+
ret = pynibs.intersection_vec_plan(ray_dir, ray_origin, plane_n, plane_p, eps=1e-6)
|
|
20
|
+
assert np.all(np.isclose(np.array([0., -20., 20.]), np.array(ret)))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if __name__ == '__main__':
|
|
24
|
+
unittest.main()
|
pynibs/tms_pulse.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def biphasic_pulse(t, R=0.0338, L=15.5*1e-6, C=193.6*1e-6, alpha=1089.8, f=2900):
|
|
5
|
+
"""
|
|
6
|
+
Returns normalized single biphasic pulse waveform of electric field (first derivative of coil current)
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
----------
|
|
10
|
+
t: ndarray of float [n_t]
|
|
11
|
+
Time array in seconds
|
|
12
|
+
R: float, optional, default: 0.0338 Ohm
|
|
13
|
+
Resistance of coil in (Ohm)
|
|
14
|
+
L: float, optional, default: 15.5*1e-6 H
|
|
15
|
+
Inductance of coil in (H)
|
|
16
|
+
C: float, optional, default: 193.6*1e-6
|
|
17
|
+
Capacitance of coil in (F)
|
|
18
|
+
alpha: float, optional, default: 1089.8 1/s
|
|
19
|
+
Damping coefficient in (1/s)
|
|
20
|
+
f: float, optional, default: 2900 Hz
|
|
21
|
+
Frequency in (Hz)
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
e: ndarray of float [n_t]
|
|
26
|
+
Normalized electric field time course (can be scaled with electric field)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
omega = 2 * np.pi * f
|
|
30
|
+
i = 1/(omega*L) * np.exp(-alpha*t) * np.sin(omega*t)
|
|
31
|
+
e = np.gradient(i)
|
|
32
|
+
e = e/np.max(e)
|
|
33
|
+
|
|
34
|
+
return i, e
|
pynibs/util/__init__.py
ADDED