pyNIBS 0.2024.8__py3-none-any.whl → 0.2026.1__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 (101) hide show
  1. pynibs/__init__.py +26 -14
  2. pynibs/coil/__init__.py +6 -0
  3. pynibs/{coil.py → coil/coil.py} +213 -543
  4. pynibs/coil/export.py +508 -0
  5. pynibs/congruence/__init__.py +4 -1
  6. pynibs/congruence/congruence.py +37 -45
  7. pynibs/congruence/ext_metrics.py +40 -11
  8. pynibs/congruence/stimulation_threshold.py +1 -2
  9. pynibs/expio/Mep.py +120 -370
  10. pynibs/expio/__init__.py +10 -0
  11. pynibs/expio/brainsight.py +34 -37
  12. pynibs/expio/cobot.py +25 -25
  13. pynibs/expio/exp.py +10 -7
  14. pynibs/expio/fit_funs.py +3 -0
  15. pynibs/expio/invesalius.py +70 -0
  16. pynibs/expio/localite.py +190 -91
  17. pynibs/expio/neurone.py +139 -0
  18. pynibs/expio/signal_ced.py +345 -2
  19. pynibs/expio/visor.py +16 -15
  20. pynibs/freesurfer.py +34 -33
  21. pynibs/hdf5_io/hdf5_io.py +149 -132
  22. pynibs/hdf5_io/xdmf.py +35 -31
  23. pynibs/mesh/__init__.py +1 -1
  24. pynibs/mesh/mesh_struct.py +77 -92
  25. pynibs/mesh/transformations.py +121 -21
  26. pynibs/mesh/utils.py +191 -99
  27. pynibs/models/_TMS.py +2 -1
  28. pynibs/muap.py +1 -2
  29. pynibs/neuron/__init__.py +10 -0
  30. pynibs/neuron/models/mep.py +566 -0
  31. pynibs/neuron/neuron_regression.py +98 -8
  32. pynibs/optimization/__init__.py +12 -2
  33. pynibs/optimization/{optimization.py → coil_opt.py} +157 -133
  34. pynibs/optimization/multichannel.py +1174 -24
  35. pynibs/optimization/workhorses.py +7 -8
  36. pynibs/regression/__init__.py +4 -2
  37. pynibs/regression/dual_node_detection.py +229 -219
  38. pynibs/regression/regression.py +92 -61
  39. pynibs/roi/__init__.py +4 -1
  40. pynibs/roi/roi_structs.py +19 -21
  41. pynibs/roi/{roi.py → roi_utils.py} +56 -33
  42. pynibs/subject.py +24 -14
  43. pynibs/util/__init__.py +20 -4
  44. pynibs/util/dosing.py +4 -5
  45. pynibs/util/quality_measures.py +39 -38
  46. pynibs/util/rotations.py +116 -9
  47. pynibs/util/{simnibs.py → simnibs_io.py} +29 -19
  48. pynibs/util/{util.py → utils.py} +20 -22
  49. pynibs/visualization/para.py +4 -4
  50. pynibs/visualization/render_3D.py +4 -4
  51. pynibs-0.2026.1.dist-info/METADATA +105 -0
  52. pynibs-0.2026.1.dist-info/RECORD +69 -0
  53. {pyNIBS-0.2024.8.dist-info → pynibs-0.2026.1.dist-info}/WHEEL +1 -1
  54. pyNIBS-0.2024.8.dist-info/METADATA +0 -723
  55. pyNIBS-0.2024.8.dist-info/RECORD +0 -107
  56. pynibs/data/configuration_exp0.yaml +0 -59
  57. pynibs/data/configuration_linear_MEP.yaml +0 -61
  58. pynibs/data/configuration_linear_RT.yaml +0 -61
  59. pynibs/data/configuration_sigmoid4.yaml +0 -68
  60. pynibs/data/network mapping configuration/configuration guide.md +0 -238
  61. pynibs/data/network mapping configuration/configuration_TEMPLATE.yaml +0 -42
  62. pynibs/data/network mapping configuration/configuration_for_testing.yaml +0 -43
  63. pynibs/data/network mapping configuration/configuration_modelTMS.yaml +0 -43
  64. pynibs/data/network mapping configuration/configuration_reg_isi_05.yaml +0 -43
  65. pynibs/data/network mapping configuration/output_documentation.md +0 -185
  66. pynibs/data/network mapping configuration/recommendations_for_accuracy_threshold.md +0 -77
  67. pynibs/data/neuron/models/L23_PC_cADpyr_biphasic_v1.csv +0 -1281
  68. pynibs/data/neuron/models/L23_PC_cADpyr_monophasic_v1.csv +0 -1281
  69. pynibs/data/neuron/models/L4_LBC_biphasic_v1.csv +0 -1281
  70. pynibs/data/neuron/models/L4_LBC_monophasic_v1.csv +0 -1281
  71. pynibs/data/neuron/models/L4_NBC_biphasic_v1.csv +0 -1281
  72. pynibs/data/neuron/models/L4_NBC_monophasic_v1.csv +0 -1281
  73. pynibs/data/neuron/models/L4_SBC_biphasic_v1.csv +0 -1281
  74. pynibs/data/neuron/models/L4_SBC_monophasic_v1.csv +0 -1281
  75. pynibs/data/neuron/models/L5_TTPC2_cADpyr_biphasic_v1.csv +0 -1281
  76. pynibs/data/neuron/models/L5_TTPC2_cADpyr_monophasic_v1.csv +0 -1281
  77. pynibs/tests/data/InstrumentMarker20200225163611937.xml +0 -19
  78. pynibs/tests/data/TriggerMarkers_Coil0_20200225163443682.xml +0 -14
  79. pynibs/tests/data/TriggerMarkers_Coil1_20200225170337572.xml +0 -6373
  80. pynibs/tests/data/Xdmf.dtd +0 -89
  81. pynibs/tests/data/brainsight_niiImage_nifticoord.txt +0 -145
  82. pynibs/tests/data/brainsight_niiImage_nifticoord_largefile.txt +0 -1434
  83. pynibs/tests/data/brainsight_niiImage_niifticoord_mixedtargets.txt +0 -47
  84. pynibs/tests/data/create_subject_testsub.py +0 -332
  85. pynibs/tests/data/data.hdf5 +0 -0
  86. pynibs/tests/data/geo.hdf5 +0 -0
  87. pynibs/tests/test_coil.py +0 -474
  88. pynibs/tests/test_elements2nodes.py +0 -100
  89. pynibs/tests/test_hdf5_io/test_xdmf.py +0 -61
  90. pynibs/tests/test_mesh_transformations.py +0 -123
  91. pynibs/tests/test_mesh_utils.py +0 -143
  92. pynibs/tests/test_nnav_imports.py +0 -101
  93. pynibs/tests/test_quality_measures.py +0 -117
  94. pynibs/tests/test_regressdata.py +0 -289
  95. pynibs/tests/test_roi.py +0 -17
  96. pynibs/tests/test_rotations.py +0 -86
  97. pynibs/tests/test_subject.py +0 -71
  98. pynibs/tests/test_util.py +0 -24
  99. /pynibs/{regression/score_types.py → neuron/models/m1_montbrio.py} +0 -0
  100. {pyNIBS-0.2024.8.dist-info → pynibs-0.2026.1.dist-info/licenses}/LICENSE +0 -0
  101. {pyNIBS-0.2024.8.dist-info → pynibs-0.2026.1.dist-info}/top_level.txt +0 -0
pynibs/hdf5_io/xdmf.py CHANGED
@@ -15,48 +15,54 @@ The module includes functions for:
15
15
 
16
16
  This module is primarily used for handling and visualizing data related to neuroimaging and brain stimulation studies.
17
17
  """
18
- import warnings
19
18
  import os
20
19
  import h5py
21
- import numpy as np
22
20
  import pynibs
21
+ import numpy as np
22
+ import warnings
23
23
 
24
24
 
25
- def write_xdmf_surf(data_hdf_fn_out, data_names, data_xdmf_fn, geo_hdf_fn, data_dims,
25
+ def write_xdmf_surf(data_hdf, data_names, data_dims, data_xdmf_fn=None, geo_hdf_fn=None,
26
26
  data_in_tris=True, data_prefix='/data/tris/'):
27
27
  """
28
28
  Write XDMF files for surfaces, such as ROIs.
29
29
 
30
30
  Parameters
31
31
  ----------
32
- data_hdf_fn_out : str
33
-
32
+ data_hdf : str
33
+ Filename of the HDF5 file containing the data.
34
34
  data_names : list of str
35
-
35
+ Names of the data arrays to be included in the XDMF file.
36
36
  data_xdmf_fn : str
37
-
37
+ Filename of the output XDMF file.
38
38
  geo_hdf_fn : str
39
-
39
+ Filename of the HDF5 file containing the geometry data.
40
40
  data_dims : list of int
41
41
  The data dimensions.
42
42
  data_in_tris : bool, default: True.
43
-
43
+ If True, the data is assumed to be in triangles. If False, it is assumed to be in tetrahedra.
44
44
  data_prefix : str, default: '/data/tris/'
45
-
45
+ Prefix for the data arrays in the HDF5 file.
46
46
 
47
47
  Returns
48
48
  -------
49
49
  <File> : .xdmf file
50
- Descriptor file pointing to geo and data .hdf5 files
50
+ Descriptor file pointing to geo and data .hdf5 files.
51
51
  """
52
52
  if not data_in_tris:
53
53
  raise NotImplementedError
54
+ if geo_hdf_fn is None:
55
+ geo_hdf_fn = data_hdf
54
56
  # if geo file exists in same folder then data file only use relative path
55
- if os.path.split(data_hdf_fn_out)[0] == os.path.split(geo_hdf_fn)[0]:
57
+ if os.path.split(data_hdf)[0] == os.path.split(geo_hdf_fn)[0]:
56
58
  geo_hdf_fn_xdmf = os.path.basename(geo_hdf_fn)
57
59
  else:
58
60
  geo_hdf_fn_xdmf = geo_hdf_fn
59
61
 
62
+ if data_xdmf_fn is None:
63
+ data_xdmf_fn = data_hdf.replace('.hdf5', '.xdmf')
64
+ data_xdmf_fn = data_xdmf_fn.replace('.h5', '.xdmf')
65
+
60
66
  with open(data_xdmf_fn, 'w') as xdmf, h5py.File(geo_hdf_fn, 'r') as h5_geo:
61
67
  # write xdmf file linking the data to the surfaces in geo_hdf_fn
62
68
  xdmf.write('<?xml version="1.0"?>\n')
@@ -116,13 +122,13 @@ def write_xdmf_surf(data_hdf_fn_out, data_names, data_xdmf_fn, geo_hdf_fn, data_
116
122
  for idx, dat in enumerate(data_dims):
117
123
  xdmf.write(f'<Attribute Name="{data_names[idx]}" AttributeType="Scalar" Center="Cell">\n')
118
124
  xdmf.write(f'<DataItem Format="HDF" Dimensions="{str(n_tris)} {str(data_dims[idx])}">\n')
119
- xdmf.write(os.path.basename(data_hdf_fn_out) + ':' + data_prefix + data_names[idx] + '\n')
125
+ xdmf.write(os.path.basename(data_hdf) + ':' + data_prefix + data_names[idx] + '\n')
120
126
  xdmf.write('</DataItem>\n')
121
127
  xdmf.write('</Attribute>\n')
122
128
 
123
129
  # tissue_type
124
- xdmf.write('<Attribute Name="tissue_type" AttributeType="Scalar" Center="Node">\n')
125
- xdmf.write('<DataItem Format="HDF" Dimensions="' + str(n_nodes) + ' 1">\n')
130
+ xdmf.write('<Attribute Name="tissue_type" AttributeType="Scalar" Center="Cell">\n')
131
+ xdmf.write('<DataItem Format="HDF" Dimensions="' + str(n_tris) + ' 1">\n')
126
132
  xdmf.write(geo_hdf_fn_xdmf + ':' + '/mesh/elm/' + lookup_str_tri + surf + '\n')
127
133
  xdmf.write('</DataItem>\n')
128
134
  xdmf.write('</Attribute>\n')
@@ -143,13 +149,13 @@ def write_xdmf(hdf5_fn, hdf5_geo_fn=None, overwrite_xdmf=False, overwrite_array=
143
149
  Parameters
144
150
  ----------
145
151
  hdf5_fn : str
146
- Filename of hdf5 file containing the data
152
+ Filename of hdf5 file containing the data.
147
153
  hdf5_geo_fn : str, optional
148
- Filename of hdf5 file containing the geometry
154
+ Filename of hdf5 file containing the geometry.
149
155
  overwrite_xdmf : bool, default: False
150
156
  Overwrite existing xdmf file if present.
151
157
  overwrite_array : bool, default: False
152
- Overwrite existing arrays if present
158
+ Overwrite existing arrays if present.
153
159
  verbose : bool
154
160
  Print output.
155
161
  mode : str, default: "r+"
@@ -159,15 +165,14 @@ def write_xdmf(hdf5_fn, hdf5_geo_fn=None, overwrite_xdmf=False, overwrite_array=
159
165
  Returns
160
166
  -------
161
167
  fn_xml : str
162
- Filename of the created .xml file
168
+ Filename of the created .xml file.
163
169
  <File> : .xdmf file
164
- hdf5_fn[-4].xdmf (only data if hdf5Geo_fn provided)
170
+ hdf5_fn[-4].xdmf (only data if hdf5Geo_fn provided).
165
171
  <File> : .hdf5 file
166
- hdf5_fn changed if neccessary
172
+ hdf5_fn changed if neccessary.
167
173
  <File> : .hdf5 file
168
- hdf5geo_fn containing spatial data
174
+ hdf5geo_fn containing spatial data.
169
175
  """
170
-
171
176
  if os.path.splitext(hdf5_fn)[1] not in ['.hdf5', '.h5', '.hdf']:
172
177
  print("Provide .hdf5 filename for existing file.")
173
178
  return
@@ -1126,7 +1131,7 @@ def create_position_path_xdmf(sorted_fn, coil_pos_fn, output_xdmf, stim_intens=N
1126
1131
  Other Parameters
1127
1132
  ----------------
1128
1133
  coil_sorted : str
1129
- Path to coil positions in sorted_fn
1134
+ Path to coil positions in sorted_fn.
1130
1135
  """
1131
1136
  # get datasets for nodes used in path, goal value, intensity
1132
1137
  sorted_data = h5py.File(sorted_fn, 'r')[coil_sorted][:]
@@ -1262,15 +1267,15 @@ def create_fibre_xdmf(fn_fibre_geo_hdf5, fn_fibre_data_hdf5=None, overwrite=True
1262
1267
  Parameters
1263
1268
  ----------
1264
1269
  fn_fibre_geo_hdf5 : str
1265
- Path to fibre_geo.hdf5 file containing the geometry (in /plot subfolder created with create_fibre_geo_hdf5())
1270
+ Path to fibre_geo.hdf5 file containing the geometry (in /plot subfolder created with create_fibre_geo_hdf5()).
1266
1271
  fn_fibre_data_hdf5 : str (optional) default: None
1267
- Path to fibre_data.hdf5 file containing the data to plot (in parent folder)
1272
+ Path to fibre_data.hdf5 file containing the data to plot (in parent folder).
1268
1273
  fibre_points_path : str (optional) default: fibre_points
1269
- Path to fibre point array in .hdf5 file
1274
+ Path to fibre point array in .hdf5 file.
1270
1275
  fibre_con_path : str (optional) default: fibre_con
1271
- Path to fibre connectivity array in .hdf5 file
1276
+ Path to fibre connectivity array in .hdf5 file.
1272
1277
  fibre_data_path : str (optional) default: ""
1273
- Path to parent data folder in data.hdf5 file (Default: no parent folder)
1278
+ Path to parent data folder in data.hdf5 file (Default: no parent folder).
1274
1279
 
1275
1280
  Returns
1276
1281
  -------
@@ -1361,7 +1366,7 @@ def data_superimpose(fn_in_hdf5_data, fn_in_geo_hdf5, fn_out_hdf5_data, data_hdf
1361
1366
  Filename of .hdf5 data output file containing the superimposed data.
1362
1367
  data_hdf5_path: str
1363
1368
  Path in .hdf5 data file where data is stored (e.g. ``'/data/tris/'``).
1364
- data_substitute: float or np.NaN, default: -1
1369
+ data_substitute: float or np.nan, default: -1
1365
1370
  Data substitute with this number for all points in the inflated brain, which do not belong to
1366
1371
  the given data set.
1367
1372
  normalize: bool or str, default: False
@@ -1376,7 +1381,6 @@ def data_superimpose(fn_in_hdf5_data, fn_in_geo_hdf5, fn_out_hdf5_data, data_hdf
1376
1381
  <File>: .hdf5 file
1377
1382
  Overlayed data.
1378
1383
  """
1379
-
1380
1384
  n_subjects = len(fn_in_hdf5_data)
1381
1385
  data_dic = [dict() for _ in range(n_subjects)]
1382
1386
  labels = [''] * n_subjects
pynibs/mesh/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from .mesh_struct import *
2
2
  from .transformations import *
3
- from .utils import *
3
+ from .utils import *
@@ -5,14 +5,12 @@ regions of interest (ROIs).
5
5
  The module includes the following classes:
6
6
 
7
7
  - `TetrahedraLinear`: This class represents a mesh consisting of linear tetrahedra. It provides methods for
8
- calculating various quantities of interest (QOIs) in the mesh, interpolating data between nodes and elements,
9
- calculating the electric field and current density, and more.
10
-
8
+ calculating various quantities of interest (QOIs) in the mesh, interpolating data between nodes and elements,
9
+ calculating the electric field and current density, and more.
11
10
  - `Mesh`: This class is a general mesh class to initialize default attributes and provides methods to fill default
12
- values based on the approach, write the mesh data to an HDF5 file, and print the mesh information.
13
-
11
+ values based on the approach, write the mesh data to an HDF5 file, and print the mesh information.
14
12
  - `ROI`: This class represents a region of interest (ROI) in the mesh. It provides methods to write the ROI data to
15
- an HDF5 file and print the ROI information.
13
+ an HDF5 file and print the ROI information.
16
14
 
17
15
  Each class and method in this module is documented with docstrings providing more detailed information about its
18
16
  purpose, parameters, and return values.
@@ -41,38 +39,37 @@ class TetrahedraLinear:
41
39
  Parameters
42
40
  ----------
43
41
  points : array of float [N_points x 3]
44
- Vertices of FE mesh
42
+ Vertices of FE mesh.
45
43
  triangles : np.ndarray of int [N_tri x 3]
46
- Connectivity of points forming triangles
44
+ Connectivity of points forming triangles.
47
45
  triangles_regions : np.ndarray of int [N_tri x 1]
48
- Region identifiers of triangles
46
+ Region identifiers of triangles.
49
47
  tetrahedra : np.ndarray of int [N_tet x 4]
50
- Connectivity of points forming tetrahedra
48
+ Connectivity of points forming tetrahedra.
51
49
  tetrahedra_regions : np.ndarray of int [N_tet x 1]
52
- Region identifiers of tetrahedra
50
+ Region identifiers of tetrahedra.
53
51
 
54
52
  Attributes
55
53
  ----------
56
54
  N_points : int
57
- Number of vertices
55
+ Number of vertices.
58
56
  N_tet : int
59
- Number of tetrahedra
57
+ Number of tetrahedra.
60
58
  N_tri : int
61
- Number of triangles
59
+ Number of triangles.
62
60
  N_region : int
63
- Number of regions
61
+ Number of regions.
64
62
  region : np.ndarray of int
65
- Region labels
63
+ Region labels.
66
64
  tetrahedra_volume : np.ndarray of float [N_tet x 1]
67
- Volumes of tetrahedra
65
+ Volumes of tetrahedra.
68
66
  tetrahedra_center : np.ndarray of float [N_tet x 1]
69
- Center of tetrahedra
67
+ Center of tetrahedra.
70
68
  triangles_center : np.ndarray of float [N_tri x 1]
71
- Center of triangles
69
+ Center of triangles.
72
70
  triangles_normal : np.ndarray of float [N_tri x 3]
73
- Normal components of triangles pointing outwards
71
+ Normal components of triangles pointing outwards.
74
72
  """
75
-
76
73
  def __init__(self, points, triangles, triangles_regions, tetrahedra, tetrahedra_regions):
77
74
  """ Initialize TetrahedraLinear class """
78
75
  self.points = points
@@ -93,7 +90,7 @@ class TetrahedraLinear:
93
90
  p2_tet = self.points[self.tetrahedra[:, 1], :]
94
91
  p3_tet = self.points[self.tetrahedra[:, 2], :]
95
92
  p4_tet = self.points[self.tetrahedra[:, 3], :]
96
- self.tetrahedra_volume = pynibs.calc_tetrahedra_volume_cross(p1_tet, p2_tet, p3_tet, p4_tet)
93
+ self.tetrahedra_volume = pynibs.mesh.calc_tetrahedra_volume_cross(p1_tet, p2_tet, p3_tet, p4_tet)
97
94
  self.tetrahedra_center = 1.0 / 4 * (p1_tet + p2_tet + p3_tet + p4_tet)
98
95
 
99
96
  else:
@@ -265,8 +262,8 @@ class TetrahedraLinear:
265
262
  # transform point data to element data
266
263
  if verbose:
267
264
  print("Transforming point data to element data")
268
- e_normal = pynibs.data_nodes2elements(data=e_normal, con=con_gm)
269
- e_tan = pynibs.data_nodes2elements(data=e_tan, con=con_gm)
265
+ e_normal = pynibs.mesh.data_nodes2elements(data=e_normal, con=con_gm)
266
+ e_tan = pynibs.mesh.data_nodes2elements(data=e_tan, con=con_gm)
270
267
 
271
268
  # crop results data to ROI
272
269
  # if not roi_mask_bool.all():
@@ -431,8 +428,8 @@ class TetrahedraLinear:
431
428
  # transform point data to element data
432
429
  if verbose:
433
430
  print("Transforming point data to element data")
434
- e_normal = pynibs.data_nodes2elements(data=e_normal, con=con_gm)
435
- e_tan = pynibs.data_nodes2elements(data=e_tan, con=con_gm)
431
+ e_normal = pynibs.mesh.data_nodes2elements(data=e_normal, con=con_gm)
432
+ e_tan = pynibs.mesh.data_nodes2elements(data=e_tan, con=con_gm)
436
433
 
437
434
  # crop results data to ROI
438
435
  # if not roi_mask_bool.all():
@@ -468,6 +465,7 @@ class TetrahedraLinear:
468
465
  - "components" : return x, y, and z component of tangential and normal components
469
466
  - "magnitude" : return magnitude of tangential and normal component (normal with sign for direction)
470
467
 
468
+
471
469
  Returns
472
470
  -------
473
471
  E_normal : np.ndarray of float
@@ -508,7 +506,7 @@ class TetrahedraLinear:
508
506
  n = n / np.tile(normal_norm, (1, 3))
509
507
 
510
508
  # interpolate magnetic vector potential to central surface points (primary electric field)
511
- # E_pri = griddata(self.points, dAdt, surf_mid, method='linear', fill_value=np.NaN, rescale=False)
509
+ # E_pri = griddata(self.points, dAdt, surf_mid, method='linear', fill_value=np.nan, rescale=False)
512
510
  if verbose:
513
511
  print("Interpolating magnetic vector potential to central surface points (primary electric field)")
514
512
  e_pri = self.calc_QOI_in_points_tet_idx(qoi=dAdt,
@@ -533,7 +531,7 @@ class TetrahedraLinear:
533
531
 
534
532
  if verbose:
535
533
  print("Determine gradient of scalar electric potential on midlayer surface (E_sec_tangential)")
536
- e_sec_tan = pynibs.calc_gradient_surface(phi=phi_surf_mid_nodes,
534
+ e_sec_tan = pynibs.mesh.calc_gradient_surface(phi=phi_surf_mid_nodes,
537
535
  points=roi.node_coord_mid,
538
536
  triangles=roi.node_number_list)
539
537
 
@@ -567,18 +565,17 @@ class TetrahedraLinear:
567
565
  Parameters
568
566
  ----------
569
567
  E : np.ndarray of float [N_tri x 3]
570
- Induced electric field given in the tetrahedra centre of the mesh instance
568
+ Induced electric field given in the tetrahedra centre of the mesh instance.
571
569
  roi : pynibs.roi.RegionOfInterestSurface
572
- RegionOfInterestSurface object class instance
570
+ RegionOfInterestSurface object class instance.
573
571
 
574
572
  Returns
575
573
  -------
576
574
  E_normal : np.ndarray of float [N_points x 3]
577
- Normal vector of electric field on GM-WM surface
575
+ Normal vector of electric field on GM-WM surface.
578
576
  E_tangential : np.ndarray of float [N_points x 3]
579
- Tangential vector of electric field on GM-WM surface
577
+ Tangential vector of electric field on GM-WM surface.
580
578
  """
581
-
582
579
  e_gm_wm_surface = E[roi.tet_idx_nodes_mid, :]
583
580
 
584
581
  # determine surface normal vector (normalized)
@@ -604,17 +601,15 @@ class TetrahedraLinear:
604
601
  Parameters
605
602
  ----------
606
603
  qoi : np.ndarray of float
607
- Quantity of interest in nodes of tetrahedra mesh instance
604
+ Quantity of interest in nodes of tetrahedra mesh instance.
608
605
  points_out : np.ndarray of float
609
- Point coordinates (x, y, z) where the qoi is going to be interpolated by linear basis functions
606
+ Point coordinates (x, y, z) where the qoi is going to be interpolated by linear basis functions.
610
607
 
611
608
  Returns
612
609
  -------
613
610
  qoi_out : np.ndarray of float
614
- Quantity of interest in points_out
615
-
611
+ Quantity of interest in points_out.
616
612
  """
617
-
618
613
  n_phi_points_out = points_out.shape[0]
619
614
  qoi_out = np.zeros(
620
615
  [n_phi_points_out, qoi.shape[1] if qoi.ndim > 1 else 1])
@@ -634,14 +629,14 @@ class TetrahedraLinear:
634
629
  # 4 points in tetrahedra
635
630
  for i in range(n_phi_points_out):
636
631
  start = time.time()
637
- vtest1 = pynibs.calc_tetrahedra_volume_cross(np.tile(points_out[i, :], (p1_all.shape[0], 1)),
632
+ vtest1 = pynibs.mesh.calc_tetrahedra_volume_cross(np.tile(points_out[i, :], (p1_all.shape[0], 1)),
638
633
  p2_all,
639
634
  p3_all,
640
635
  p4_all)
641
636
  tet_idx_bool_1 = (vtest1 >= 0)
642
637
  tet_idx_1 = np.nonzero(tet_idx_bool_1)[0]
643
638
 
644
- vtest2 = pynibs.calc_tetrahedra_volume_cross(p1_all[tet_idx_1, :],
639
+ vtest2 = pynibs.mesh.calc_tetrahedra_volume_cross(p1_all[tet_idx_1, :],
645
640
  np.tile(
646
641
  points_out[i, :], (tet_idx_1.shape[0], 1)),
647
642
  p3_all[tet_idx_1, :],
@@ -649,7 +644,7 @@ class TetrahedraLinear:
649
644
  tet_idx_bool_2 = (vtest2 >= 0)
650
645
  tet_idx_2 = tet_idx_1[np.nonzero(tet_idx_bool_2)[0]]
651
646
 
652
- vtest3 = pynibs.calc_tetrahedra_volume_cross(p1_all[tet_idx_2, :],
647
+ vtest3 = pynibs.mesh.calc_tetrahedra_volume_cross(p1_all[tet_idx_2, :],
653
648
  p2_all[tet_idx_2, :],
654
649
  np.tile(
655
650
  points_out[i, :], (tet_idx_2.shape[0], 1)),
@@ -657,7 +652,7 @@ class TetrahedraLinear:
657
652
  tet_idx_bool_3 = (vtest3 >= 0)
658
653
  tet_idx_3 = tet_idx_2[np.nonzero(tet_idx_bool_3)[0]]
659
654
 
660
- vtest4 = pynibs.calc_tetrahedra_volume_cross(p1_all[tet_idx_3, :],
655
+ vtest4 = pynibs.mesh.calc_tetrahedra_volume_cross(p1_all[tet_idx_3, :],
661
656
  p2_all[tet_idx_3, :],
662
657
  p3_all[tet_idx_3, :],
663
658
  np.tile(points_out[i, :], (tet_idx_3.shape[0], 1)))
@@ -665,19 +660,19 @@ class TetrahedraLinear:
665
660
  tet_idx = tet_idx_3[np.nonzero(tet_idx_bool_4)[0]]
666
661
 
667
662
  # calculate subvolumes of final tetrahedron and its total volume
668
- vsub1 = pynibs.calc_tetrahedra_volume_cross(points_out[i, :][np.newaxis],
663
+ vsub1 = pynibs.mesh.calc_tetrahedra_volume_cross(points_out[i, :][np.newaxis],
669
664
  p2_all[tet_idx, :],
670
665
  p3_all[tet_idx, :],
671
666
  p4_all[tet_idx, :])
672
- vsub2 = pynibs.calc_tetrahedra_volume_cross(p1_all[tet_idx, :],
667
+ vsub2 = pynibs.mesh.calc_tetrahedra_volume_cross(p1_all[tet_idx, :],
673
668
  points_out[i, :][np.newaxis],
674
669
  p3_all[tet_idx, :],
675
670
  p4_all[tet_idx, :])
676
- vsub3 = pynibs.calc_tetrahedra_volume_cross(p1_all[tet_idx, :],
671
+ vsub3 = pynibs.mesh.calc_tetrahedra_volume_cross(p1_all[tet_idx, :],
677
672
  p2_all[tet_idx, :],
678
673
  points_out[i, :][np.newaxis],
679
674
  p4_all[tet_idx, :])
680
- vsub4 = pynibs.calc_tetrahedra_volume_cross(p1_all[tet_idx, :],
675
+ vsub4 = pynibs.mesh.calc_tetrahedra_volume_cross(p1_all[tet_idx, :],
681
676
  p2_all[tet_idx, :],
682
677
  p3_all[tet_idx, :],
683
678
  points_out[i, :][np.newaxis], )
@@ -700,19 +695,17 @@ class TetrahedraLinear:
700
695
  Parameters
701
696
  ----------
702
697
  qoi : np.ndarray of float
703
- Quantity of interest in nodes of tetrahedra mesh instance
698
+ Quantity of interest in nodes of tetrahedra mesh instance.
704
699
  points_out : np.ndarray of float
705
- Point coordinates (x, y, z) where the qoi is going to be interpolated by linear basis functions
700
+ Point coordinates (x, y, z) where the qoi is going to be interpolated by linear basis functions.
706
701
  tet_idx : np.ndarray of int
707
- Element indices where the points_out are sitting
702
+ Element indices where the points_out are sitting.
708
703
 
709
704
  Returns
710
705
  -------
711
706
  qoi_out : np.ndarray of float
712
707
  Quantity of interest in points_out
713
-
714
708
  """
715
-
716
709
  n_phi_points_out = points_out.shape[0]
717
710
  qoi_out = np.zeros([n_phi_points_out, qoi.shape[1] if qoi.ndim > 1 else 1])
718
711
 
@@ -722,19 +715,19 @@ class TetrahedraLinear:
722
715
  p4_all = self.points[self.tetrahedra[:, 3], :]
723
716
 
724
717
  # determine sub-volumes
725
- vsub1 = pynibs.calc_tetrahedra_volume_cross(points_out,
718
+ vsub1 = pynibs.mesh.calc_tetrahedra_volume_cross(points_out,
726
719
  p2_all[tet_idx, :],
727
720
  p3_all[tet_idx, :],
728
721
  p4_all[tet_idx, :])
729
- vsub2 = pynibs.calc_tetrahedra_volume_cross(p1_all[tet_idx, :],
722
+ vsub2 = pynibs.mesh.calc_tetrahedra_volume_cross(p1_all[tet_idx, :],
730
723
  points_out,
731
724
  p3_all[tet_idx, :],
732
725
  p4_all[tet_idx, :])
733
- vsub3 = pynibs.calc_tetrahedra_volume_cross(p1_all[tet_idx, :],
726
+ vsub3 = pynibs.mesh.calc_tetrahedra_volume_cross(p1_all[tet_idx, :],
734
727
  p2_all[tet_idx, :],
735
728
  points_out,
736
729
  p4_all[tet_idx, :])
737
- vsub4 = pynibs.calc_tetrahedra_volume_cross(p1_all[tet_idx, :],
730
+ vsub4 = pynibs.mesh.calc_tetrahedra_volume_cross(p1_all[tet_idx, :],
738
731
  p2_all[tet_idx, :],
739
732
  p3_all[tet_idx, :],
740
733
  points_out)
@@ -747,19 +740,19 @@ class TetrahedraLinear:
747
740
 
748
741
  # for i in range(N_phi_points_out):
749
742
  # # calculate subvolumes of final tetrahedron and its total volume
750
- # Vsub1 = pynibs.calc_tetrahedra_volume_cross(points_out[i, :][np.newaxis],
743
+ # Vsub1 = pynibs.mesh.calc_tetrahedra_volume_cross(points_out[i, :][np.newaxis],
751
744
  # p2_all[tet_idx[i], :][np.newaxis],
752
745
  # p3_all[tet_idx[i], :][np.newaxis],
753
746
  # P4_all[tet_idx[i], :][np.newaxis])
754
- # vsub2 = pynibs.calc_tetrahedra_volume_cross(P1_all[tet_idx[i], :][np.newaxis],
747
+ # vsub2 = pynibs.mesh.calc_tetrahedra_volume_cross(P1_all[tet_idx[i], :][np.newaxis],
755
748
  # points_out[i, :][np.newaxis],
756
749
  # p3_all[tet_idx[i], :][np.newaxis],
757
750
  # P4_all[tet_idx[i], :][np.newaxis])
758
- # Vsub3 = pynibs.calc_tetrahedra_volume_cross(P1_all[tet_idx[i], :][np.newaxis],
751
+ # Vsub3 = pynibs.mesh.calc_tetrahedra_volume_cross(P1_all[tet_idx[i], :][np.newaxis],
759
752
  # p2_all[tet_idx[i], :][np.newaxis],
760
753
  # points_out[i, :][np.newaxis],
761
754
  # P4_all[tet_idx[i], :][np.newaxis])
762
- # Vsub4 = pynibs.calc_tetrahedra_volume_cross(P1_all[tet_idx[i], :][np.newaxis],
755
+ # Vsub4 = pynibs.mesh.calc_tetrahedra_volume_cross(P1_all[tet_idx[i], :][np.newaxis],
763
756
  # p2_all[tet_idx[i], :][np.newaxis],
764
757
  # p3_all[tet_idx[i], :][np.newaxis],
765
758
  # points_out[i, :][np.newaxis])
@@ -778,12 +771,12 @@ class TetrahedraLinear:
778
771
  Parameters
779
772
  ----------
780
773
  data : np.ndarray [N_nodes x N_data]
781
- Data in nodes
774
+ Data in nodes.
782
775
 
783
776
  Returns
784
777
  -------
785
778
  data_elements : np.ndarray [N_elements x N_data]
786
- Data in elements
779
+ Data in elements.
787
780
  """
788
781
  data_elements = np.sum(data[self.tetrahedra[:, i]] for i in range(4)) / 4.0
789
782
 
@@ -791,19 +784,19 @@ class TetrahedraLinear:
791
784
 
792
785
  def data_elements2nodes(self, data):
793
786
  """
794
- Transforms an data in tetrahedra into the nodes after Zienkiewicz et al. (1992) [1]_.
787
+ Transforms data in tetrahedra into the nodes after Zienkiewicz et al. (1992) [1]_.
795
788
  Can only transform volume data, i.e. needs the data in the surrounding tetrahedra to average it to the nodes.
796
789
  Will not work well for discontinuous fields (like E, if several tissues are used).
797
790
 
798
791
  Parameters
799
792
  ----------
800
793
  data : np.ndarray [N_elements x N_data]
801
- Data in tetrahedra
794
+ Data in tetrahedra.
802
795
 
803
796
  Returns
804
797
  -------
805
798
  data_nodes : np.ndarray [N_nodes x N_data]
806
- Data in nodes
799
+ Data in nodes.
807
800
 
808
801
  Notes
809
802
  -----
@@ -811,7 +804,6 @@ class TetrahedraLinear:
811
804
  posteriori error estimates. Part 1: The recovery technique." International Journal for
812
805
  Numerical Methods in Engineering 33.7 (1992): 1331-1364.
813
806
  """
814
-
815
807
  # check dimension of input data
816
808
  if data.ndim == 1:
817
809
  data = data[:, np.newaxis]
@@ -897,14 +889,13 @@ class TetrahedraLinear:
897
889
  Parameters
898
890
  ----------
899
891
  tetrahedra_indices : np.ndarray
900
- Indices of the tetrehedra where the outer volume is to be determined (default: all tetrahedra)
892
+ Indices of the tetrahedra where the outer volume is to be determined (default: all tetrahedra).
901
893
 
902
894
  Returns
903
895
  -------
904
896
  faces : np.ndarray
905
- List of nodes in faces in arbitrary order
897
+ List of nodes in faces in arbitrary order.
906
898
  """
907
-
908
899
  if tetrahedra_indices is None:
909
900
  tetrahedra_indices = self.tetrahedra_index
910
901
 
@@ -930,14 +921,13 @@ class TetrahedraLinear:
930
921
  Parameters
931
922
  ----------
932
923
  phi : np.ndarray of float [N_nodes]
933
- Scalar DOF the gradient is calculated for
924
+ Scalar DOF the gradient is calculated for.
934
925
 
935
926
  Returns
936
927
  -------
937
928
  grad_phi : np.ndarray of float [N_tet x 3]
938
- Gradient of Scalar DOF in tetrahedra center
929
+ Gradient of Scalar DOF in tetrahedra center.
939
930
  """
940
-
941
931
  a1 = np.vstack((self.points[self.tetrahedra[:, 3], :] - self.points[self.tetrahedra[:, 1], :],
942
932
  self.points[self.tetrahedra[:, 2], :] -
943
933
  self.points[self.tetrahedra[:, 0], :],
@@ -1033,7 +1023,7 @@ class TetrahedraLinear:
1033
1023
  Returns
1034
1024
  -------
1035
1025
  <File> : .txt file
1036
- Element indices of the tetrahedra touching the surfaces (outer-most elements)
1026
+ Element indices of the tetrahedra touching the surfaces (outer-most elements).
1037
1027
  """
1038
1028
  # determine indices of the 2 adjacent tetrahedra with common face on
1039
1029
  # surface
@@ -1121,25 +1111,24 @@ class TetrahedraLinear:
1121
1111
  Parameters
1122
1112
  ----------
1123
1113
  E : np.ndarray of float [N_tri x 3]
1124
- Electric field data on surfaces
1114
+ Electric field data on surfaces.
1125
1115
  fname : str
1126
1116
  Filename of the .txt file containing the tetrahedra indices, which are adjacent to the surface triangles
1127
- generated by the method "calc_surface_adjacent_tetrahedra_idx_list(self, fname)"
1117
+ generated by the method "calc_surface_adjacent_tetrahedra_idx_list(self, fname)".
1128
1118
 
1129
1119
  Returns
1130
1120
  -------
1131
1121
  En_pos : np.ndarray of float [N_tri x 3]
1132
- Normal component of electric field of top side (outside) of surface
1122
+ Normal component of electric field of top side (outside) of surface.
1133
1123
  En_neg : np.ndarray of float [N_tri x 3]
1134
- Normal component of electric field of bottom side (inside) of surface
1124
+ Normal component of electric field of bottom side (inside) of surface.
1135
1125
  n : np.ndarray of float [N_tri x 3]
1136
1126
  Normal vector
1137
1127
  Et : np.ndarray of float [N_tri x 3]
1138
- Tangential component of electric field lying in surface
1128
+ Tangential component of electric field lying in surface.
1139
1129
  t : np.ndarray of float [N_tri x 3]
1140
- Tangential vector
1130
+ Tangential vector.
1141
1131
  """
1142
-
1143
1132
  n = self.triangles_normal
1144
1133
  en_pos = np.zeros((self.N_tri, 1))
1145
1134
  en_neg = np.zeros((self.N_tri, 1))
@@ -1166,25 +1155,24 @@ class TetrahedraLinear:
1166
1155
 
1167
1156
  def get_faces(self, tetrahedra_indexes=None):
1168
1157
  """
1169
- Creates a list of nodes in each face and a list of faces in each tetrahedra.
1158
+ Creates a list of nodes in each face and a list of faces in each tetrahedron.
1170
1159
 
1171
1160
  Parameters
1172
1161
  ----------
1173
1162
  tetrahedra_indexes : np.ndarray
1174
- Indices of the tetrehedra where the faces are to be determined (default: all tetrahedra)
1163
+ Indices of the tetrehedra where the faces are to be determined (default: all tetrahedra).
1175
1164
 
1176
1165
  Returns
1177
1166
  -------
1178
1167
  faces : np.ndarray
1179
1168
  List of nodes in faces, in arbitrary order
1180
- th_faces : np.ndarray
1181
- List of faces in each tetrahedra, starts at 0, order=((0, 2, 1), (0, 1, 3), (0, 3, 2), (1, 2, 3))
1169
+ th_faces : np.ndarray.
1170
+ List of faces in each tetrahedron, starts at 0, order=((0, 2, 1), (0, 1, 3), (0, 3, 2), (1, 2, 3)).
1182
1171
  face_adjacency_list : np.ndarray
1183
1172
  List of tetrahedron adjacent to each face, filled with -1 if a face is in a
1184
1173
  single tetrahedron. Not in the normal element ordering, but only in the order
1185
- the tetrahedra are presented
1174
+ the tetrahedra are presented.
1186
1175
  """
1187
-
1188
1176
  if tetrahedra_indexes is None:
1189
1177
  tetrahedra_indexes = np.arange(self.tetrahedra.shape[0])
1190
1178
  # th = self[tetrahedra_indexes]
@@ -1225,10 +1213,9 @@ class TetrahedraLinear:
1225
1213
 
1226
1214
 
1227
1215
  class Mesh:
1228
- """"
1216
+ """
1229
1217
  Mesh class to initialize default attributes.
1230
1218
  """
1231
-
1232
1219
  def __init__(self, mesh_name, subject_id, subject_folder):
1233
1220
  self.subject_id = subject_id
1234
1221
  self.subject_folder = subject_folder
@@ -1277,6 +1264,7 @@ class Mesh:
1277
1264
  'headreco'
1278
1265
  'mri2mesh'
1279
1266
  'charm'
1267
+
1280
1268
  """
1281
1269
  self.approach = approach
1282
1270
  if approach == 'headreco':
@@ -1317,9 +1305,8 @@ class Mesh:
1317
1305
  check_file_exist : bool
1318
1306
  Check if provided filenames exist, warn if not.
1319
1307
  verbose : bool
1320
- Print self information
1308
+ Print self information.
1321
1309
  """
1322
-
1323
1310
  pynibs.write_dict_to_hdf5(fn_hdf5=fn_hdf5, data=self.__dict__, folder=f"mesh/{self.name}",
1324
1311
  check_file_exist=check_file_exist)
1325
1312
  if verbose:
@@ -1340,7 +1327,6 @@ class ROI:
1340
1327
  """
1341
1328
  Region of interest class to initialize default attributes.
1342
1329
  """
1343
-
1344
1330
  def __init__(self, subject_id, roi_name, mesh_name):
1345
1331
  self.subject_id = subject_id
1346
1332
  self.name = roi_name
@@ -1375,9 +1361,8 @@ class ROI:
1375
1361
  check_file_exist : bool
1376
1362
  Check if provided filenames exist, warn if not.
1377
1363
  verbose : bool
1378
- Print self information
1364
+ Print self information.
1379
1365
  """
1380
-
1381
1366
  pynibs.write_dict_to_hdf5(fn_hdf5=fn_hdf5, data=self.__dict__, folder=f"roi/{self.mesh_name}/{self.name}",
1382
1367
  check_file_exist=check_file_exist)
1383
1368
  if verbose: