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
pynibs/util/dosing.py ADDED
@@ -0,0 +1,233 @@
1
+ """
2
+ Functions used in our perspective on e-field based TMS dosing[1]_
3
+
4
+ References
5
+ ----------
6
+ .. [1] Numssen, O., Kuhnke, P., Weise, K., & Hartwigsen, G. (2024).
7
+ Electric-field-based dosing for TMS. Imaging Neuroscience, 2, 1-12.
8
+ DOI: 10.1162/imag_a_00106
9
+ """
10
+ import os
11
+ import h5py
12
+ import numpy as np
13
+ import pynibs
14
+
15
+
16
+ def get_intensity_e(e1, e2, target1, target2, radius1, radius2, headmesh,
17
+ rmt=1, roi='midlayer_lh_rh', verbose=False):
18
+ """
19
+ Computes the stimulator intensity adjustment factor based on the electric field.
20
+
21
+ Parameters
22
+ ----------
23
+ e1 : str
24
+ .hdf5 e field with midlayer.
25
+ e2 : str
26
+ .hdf5 e field with midlayer.
27
+ target1 : np.ndarray (3,)
28
+ Coordinates of cortical site of MT.
29
+ target2 : np.ndarray (3,)
30
+ Coordinates of cortical target site.
31
+ radius1 : float
32
+ Electric field of field1 is averaged over elements inside this radius around target1.
33
+ radius2 : float
34
+ Electric field of field2 is averaged over elements inside this radius around target2.
35
+ headmesh : str
36
+ .hdf5 headmesh.
37
+ rmt : float, default: 1
38
+ Resting motor threshold to be corrected.
39
+ roi : str, default: 'midlayer_lh_rh'
40
+ Name of roi. Expected to sit in ``mesh['/data/midlayer/roi_surface/']``.
41
+ verbose : bool, default: False
42
+ Flag indicating verbosity.
43
+
44
+ Returns
45
+ -------
46
+ rmt_e_corr : float
47
+ Adjusted stimulation intensity for target2.
48
+ """
49
+
50
+ with h5py.File(headmesh, 'r') as f:
51
+ tris = f[f'/roi_surface/{roi}/tri_center_coord_mid'][:]
52
+
53
+ idx, e_avg_target, e_target, t_idx_sphere = [], [], [], []
54
+ for field, target, radius in zip([e1, e2], [target1, target2], [radius1, radius2]):
55
+ idx.append(np.argmin(np.linalg.norm(tris - target, axis=1)))
56
+ t_idx_sphere.append(np.where(np.linalg.norm(tris - tris[idx[-1]], axis=1) < radius)[0])
57
+ with h5py.File(field, 'r') as e:
58
+ e_avg_target.append(np.mean(e[f'/data/midlayer/roi_surface/{roi}/E_mag'][t_idx_sphere[-1]]))
59
+ e_target.append(e[f'/data/midlayer/roi_surface/{roi}/E_mag'][idx[-1]])
60
+
61
+ # determine scaling factor
62
+ e_fac_avg = e_avg_target[0] / e_avg_target[1]
63
+ e_fac = e_target[0] / e_target[1]
64
+ rmt_e_corr = rmt * e_fac_avg
65
+
66
+ if verbose:
67
+ print(f"Target1: {target1}->{tris[idx[0]]}. E: {e_target[0]:2.4f}, {len(t_idx_sphere[0])} elms")
68
+ print(f"Target2: {target2}->{tris[idx[1]]}. E: {e_target[1]:2.4f}, {len(t_idx_sphere[0])} elms")
69
+ print(f"Efield normalization factor: {e_fac_avg:2.4f} ({e_fac:2.4f} for single elm).")
70
+ # print(f"Center: {target} { tris_center[t_idx, ]}.")
71
+ print(f"Given intensity {rmt}% is normalized to {rmt * e_fac_avg:2.4f}%.")
72
+
73
+ return rmt_e_corr
74
+
75
+
76
+ def get_intensity_e_old(mesh1, mesh2, target1, target2, radius1, radius2, rmt=1, verbose=False):
77
+ """
78
+ Computes the stimulator intensity adjustment factor based on the electric field.
79
+ Something weird is going on here - check simnibs coordinates of midlayer before usage.
80
+
81
+ Parameters
82
+ ----------
83
+ mesh1 : str or simnibs.msh.mesh_io.Msh
84
+ Midlayer mesh containing results of the optimal coil position of MT in the midlayer
85
+ (e.g.: .../subject_overlays/00001.hd_fixed_TMS_1-0001_MagVenture_MCF_B65_REF_highres.ccd_scalar_central.msh)
86
+ mesh2 : str or simnibs.msh.mesh_io.Msh
87
+ Midlayer mesh containing results of the optimal coil position of the target in the midlayer
88
+ (e.g.: .../subject_overlays/00001.hd_fixed_TMS_1-0001_MagVenture_MCF_B65_REF_highres.ccd_scalar_central.msh)
89
+ target1 : np.ndarray
90
+ (3,) Coordinates of cortical site of MT.
91
+ target2 : np.ndarray
92
+ (3,) Coordinates of cortical target site.
93
+ radius1 : float
94
+ Electric field in target 1 is averaged over elements inside this radius.
95
+ radius2 : float
96
+ Electric field in target 2 is averaged over elements inside this radius.
97
+ rmt : float, default: 1
98
+ Resting motor threshold, which will be corrected.
99
+ verbose : bool, default: False
100
+ Flag indicating verbosity.
101
+
102
+ Returns
103
+ -------
104
+ rmt_e_corr : float
105
+ Adjusted stimulation intensity for target2.
106
+ """
107
+ from simnibs.msh.mesh_io import read_msh
108
+
109
+ # load mesh1 (MT) if filename is provided
110
+ if isinstance(mesh1, str):
111
+ if os.path.splitext(mesh1)[1] == ".msh":
112
+ mesh1 = read_msh(mesh1)
113
+ elif os.path.splitext(mesh1)[1] == ".hdf5":
114
+ mesh1 = pynibs.load_mesh_hdf5(mesh1)
115
+
116
+ # load mesh2 (target) if filename is provided
117
+ if isinstance(mesh2, str):
118
+ if os.path.splitext(mesh2)[1] == ".msh":
119
+ mesh2 = read_msh(mesh2)
120
+ elif os.path.splitext(mesh2)[1] == ".hdf5":
121
+ mesh2 = pynibs.load_mesh_hdf5(mesh2)
122
+
123
+ # load electric fields in midlayer and average electric field around sphere in targets
124
+ e_avg_target = []
125
+ for mesh, target, radius in zip([mesh1, mesh2], [target1, target2], [radius1, radius2]):
126
+ nodes = mesh.nodes.node_coord
127
+ tris = mesh.elm.node_number_list[:, :-1] - 1
128
+ tris_center = np.mean(nodes[tris,], axis=1)
129
+
130
+ e_norm_nodes = None
131
+ for nodedata in mesh.nodedata:
132
+ if nodedata.field_name == "E_norm":
133
+ e_norm_nodes = nodedata.value
134
+
135
+ e_norm_tris = np.mean(e_norm_nodes[tris], axis=1)
136
+
137
+ # project targets to midlayer
138
+ t_idx = np.argmin(np.linalg.norm(tris_center - target, axis=1))
139
+
140
+ # get indices of surrounding elements in some radius
141
+ t_idx_sphere = np.where(np.linalg.norm(tris_center - tris_center[t_idx,], axis=1) < radius)[0]
142
+
143
+ # average e-field in this area
144
+ e_avg_target.append(np.mean(e_norm_tris[t_idx_sphere]))
145
+
146
+ print(f"Center: {target} {tris_center[t_idx,]}.")
147
+
148
+ # determine scaling factor
149
+ e_fac = e_avg_target[0] / e_avg_target[1]
150
+ rmt_e_corr = rmt * e_fac
151
+
152
+ if verbose:
153
+ print(f"Efield normalized factor is: {e_fac:2.4f}.")
154
+ # print(f"Center: {target} { tris_center[t_idx, ]}.")
155
+ print(f"Given stimulatior intensity {rmt}% is normalized to new intensity {rmt * e_fac:2.4f}%.")
156
+
157
+ return rmt_e_corr
158
+
159
+
160
+ def get_intensity_stokes(mesh, target1, target2, spat_grad=3, rmt=0, scalp_tag=1005, roi=None, verbose=False):
161
+ """
162
+ Computes the stimulator intensity adjustment factor according to Stokes et al. 2005
163
+ (doi:10.1152/jn.00067.2005).
164
+ Adjustment is based on target-scalp distance differences:
165
+ adj = (Dist2-Dist1)*spat_grad
166
+
167
+ Parameters
168
+ ----------
169
+ mesh : str or simnibs.msh.mesh_io.Msh
170
+ Mesh of the head model.
171
+ target1 : np.ndarray
172
+ (3,) Coordinates of cortical site of MT.
173
+ target2 : np.ndarray
174
+ (3,) Coordinates of cortical target site.
175
+ spat_grad : float, default: 3
176
+ Spatial gradient.
177
+ rmt : float, default: 0
178
+ Resting motor threshold, which will be corrected.
179
+ scalp_tag: int, default: 1005
180
+ Tag in the mesh where the scalp is to be set.
181
+ roi: np.ndarray, optional
182
+ (3,N) Array of nodes to project targets onto.
183
+ verbose : bool, default: False
184
+ Print verbosity information.
185
+
186
+ Returns
187
+ -------
188
+ rmt_stokes : float
189
+ Adjusted stimulation intensity for target2.
190
+ """
191
+ from simnibs.msh.mesh_io import read_msh
192
+ from pynibs.mesh import project_on_scalp
193
+
194
+ # load mesh if filename is provided
195
+ if isinstance(mesh, str):
196
+ if os.path.splitext(mesh)[1] == ".msh":
197
+ mesh = read_msh(mesh)
198
+ elif os.path.splitext(mesh)[1] == ".hdf5":
199
+ mesh = pynibs.load_mesh_hdf5(mesh)
200
+
201
+ t1_proj = project_on_scalp(target1, mesh, scalp_tag=scalp_tag)
202
+ t2_proj = project_on_scalp(target2, mesh, scalp_tag=scalp_tag)
203
+
204
+ if roi is not None:
205
+ t1_idx = np.argmin(np.linalg.norm(roi - target1, axis=1))
206
+ t2_idx = np.argmin(np.linalg.norm(roi - target2, axis=1))
207
+ t1_on_roi = roi[t1_idx]
208
+ t2_on_roi = roi[t2_idx]
209
+
210
+ if verbose:
211
+ print("Projecting targets on ROI:\n"
212
+ "T1: [{0:+06.2f}, {1:+06.2f}, {2:+06.2f}] -> [{3:+06.2f}, {4:+06.2f}, {5:+06.2f}] Dist: {6:05.2f}mm"
213
+ "\n".format(*target1, *t1_on_roi, np.linalg.norm(target1 - t1_on_roi)) + \
214
+ "T2: [{0:+06.2f}, {1:+06.2f}, {2:+06.2f}] -> [{3:+06.2f}, {4:+06.2f}, {5:+06.2f}] Dist: {6:05.2f}mm"
215
+ "".format(*target2, *t2_on_roi, np.linalg.norm(target2 - t2_on_roi)))
216
+ target1 = t1_on_roi
217
+ target2 = t2_on_roi
218
+
219
+ t1_dist = np.linalg.norm(target1 - t1_proj)
220
+ t2_dist = np.linalg.norm(target2 - t2_proj)
221
+
222
+ stokes_factor = (t2_dist - t1_dist) * spat_grad
223
+ rmt_stokes = rmt + stokes_factor
224
+
225
+ if verbose:
226
+ print("Target 1: [{0:+06.2f}, {1:+06.2f}, {2:+06.2f}] ->"
227
+ " [{3:+06.2f}, {4:+06.2f}, {5:+06.2f}] Dist: {6:05.2f}mm ".format(*target1, *t1_proj.flatten(), t1_dist))
228
+ print("Target 2: [{0:+06.2f}, {1:+06.2f}, {2:+06.2f}] ->"
229
+ " [{3:+06.2f}, {4:+06.2f}, {5:+06.2f}] Dist: {6:05.2f}mm ".format(*target2, *t2_proj.flatten(), t2_dist))
230
+ print(f"Dist1 - Dist2: {t1_dist - t2_dist:05.2f} mm")
231
+ print(f"rMT Stokes corrected: {rmt_stokes:05.2f} %MSO")
232
+
233
+ return rmt_stokes