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/subject.py ADDED
@@ -0,0 +1,1009 @@
1
+ import os
2
+ import h5py
3
+ import pickle
4
+ import warnings
5
+ import numpy as np
6
+ import pynibs
7
+
8
+
9
+ class Subject:
10
+ """
11
+ Subject containing subject specific information, like mesh, roi, uncertainties, plot settings.
12
+
13
+ Attributes
14
+ ----------
15
+ self.id : str
16
+ Subject id.
17
+
18
+ Notes
19
+ -----
20
+ **Initialization**
21
+
22
+ .. code-block:: python
23
+
24
+ sub = pynibs.subject(subject_ID, mesh)
25
+
26
+ **Parameters**
27
+
28
+ id : str
29
+ Subject id.
30
+ fn_mesh : str
31
+ .msh or .hdf5 file containing the mesh information.
32
+
33
+ **Subject.seg, segmentation information dictionary**
34
+
35
+ fn_lh_wm : str
36
+ Filename of left hemisphere white matter surface.
37
+ fn_rh_wm : str
38
+ Filename of right hemisphere white matter surface.
39
+ fn_lh_gm : str
40
+ Filename of left hemisphere grey matter surface.
41
+ fn_rh_gm : str
42
+ Filename of right hemisphere grey matter surface.
43
+ fn_lh_curv : str
44
+ Filename of left hemisphere curvature data on grey matter surface.
45
+ fn_rh_curv : str
46
+ Filename of right hemisphere curvature data on grey matter surface.
47
+
48
+ **Subject.mri, mri information dictionary**
49
+
50
+ fn_mri_T1 : str
51
+ Filename of T1 image.
52
+ fn_mri_T2 : str
53
+ Filename of T2 image.
54
+ fn_mri_DTI : str
55
+ Filename of DTI dataset.
56
+ fn_mri_DTI_bvec : str
57
+ Filename of DTI bvec file.
58
+ fn_mri_DTI_bval : str
59
+ Filename of DTI bval file.
60
+ fn_mri_conform : str
61
+ Filename of conform T1 image resulting from SimNIBS mri2mesh function.
62
+
63
+ **Subject.ps, plot settings dictionary**
64
+
65
+ see plot functions in para.py for more details
66
+
67
+ **Subject.exp, experiment dictionary**
68
+
69
+ info : str
70
+ General information about the experiment.
71
+ date : str
72
+ Date of experiment (e.g. 01/01/2018).
73
+ fn_tms_nav : str
74
+ Path to TMS navigator folder.
75
+ fn_data : str
76
+ Path to data folder or files.
77
+ fn_exp_csv : str
78
+ Filename of experimental data .csv file containing the merged experimental data information.
79
+ fn_coil : str
80
+ Filename of .ccd or .nii file of coil used in the experiment (contains ID).
81
+ fn_mri_nii : str
82
+ Filename of MRI .nii file used during the experiment.
83
+ cond : str or list of str
84
+ Conditions in the experiment in the recorded order (e.g. ['PA-45', 'PP-00']).
85
+ experimenter : str
86
+ Name of experimenter who conducted the experiment.
87
+ incidents : str
88
+ Description of special events occurred during the experiment.
89
+
90
+ **Subject.mesh, mesh dictionary**
91
+
92
+ info : str
93
+ Information about the mesh (e.g. dicretization, etc.).
94
+ fn_mesh_msh : str
95
+ Filename of the .msh file containing the FEM mesh.
96
+ fn_mesh_hdf5 : str
97
+ Filename of the .hdf5 file containing the FEM mesh.
98
+ seg_idx : int
99
+ Index indicating to which segmentation dictionary the mesh belongs.
100
+
101
+ **Subject.roi region of interest dictionary**
102
+
103
+ type : str
104
+ Specify type of ROI ('surface', 'volume')
105
+ info : str
106
+ Info about the region of interest, e.g. "M1 midlayer from freesurfer mask xyz".
107
+ region : list of str or float
108
+ Filename for freesurfer mask or ``[[X_min, X_max], [Y_min, Y_max], [Z_min, Z_max]]``.
109
+ delta : float
110
+ Distance parameter between WM and GM (0 -> WM, 1 -> GM) (for surfaces only).
111
+ """
112
+ def __init__(self, subject_id, subject_folder):
113
+ self.id = subject_id # subject id
114
+ self.subject_folder = subject_folder # folder containing subject information
115
+ self.mri = [] # list containing mri information
116
+ self.mesh = {} # dict containing mesh information
117
+ self.exp = {} # dict containing information about conducted experiments
118
+ self.ps = [] # list containing plot settings
119
+ self.roi = {} # dict containing the roi information
120
+
121
+ def __str__(self):
122
+ """ Overload method to allow print(subject_object)"""
123
+ ret = f'{"=" * 64}\n' \
124
+ f'Subject ID : {self.id}\n' \
125
+ f'Folder : {self.subject_folder}\n' \
126
+ f'Meshes : {len(self.mesh.items())}\n' \
127
+ f'Experiments: {len(self.exp.items())}\n'
128
+ ret += f'{"=" * 64}\n\n'
129
+ ret += '------------------------------MRI------------------------------\n'
130
+ if not self.mri:
131
+ ret += 'None\n'
132
+ else:
133
+ for idx_mesh, mri in enumerate(self.mri):
134
+ ret += f"|- MRI[{idx_mesh}]\n"
135
+ for k, d in mri.items():
136
+ if k == list(mri.keys())[-1]:
137
+ pref = '└-'
138
+ else:
139
+ pref = '|-'
140
+ ret += f' {pref} {k: >19}: {d}\n'
141
+
142
+ ret += '\n------------------------------MESH------------------------------\n'
143
+ if not self.mesh:
144
+ ret += 'None\n'
145
+ else:
146
+ for idx_mesh, mesh in self.mesh.items():
147
+ ret += f"|- Mesh name: {idx_mesh}\n"
148
+
149
+ # plot roi keys
150
+ if idx_mesh in self.roi.keys():
151
+ for r_idx, roi in self.roi[idx_mesh].items():
152
+ key_list_roi = []
153
+ ret += f' |-- ROI: {r_idx}\n'
154
+ for k, d in roi.items():
155
+ if d is not None and k != 'name' and k != 'mesh_name':
156
+ key_list_roi.append(k)
157
+ for k in key_list_roi:
158
+ if k == key_list_roi[-1]:
159
+ pref = '└-'
160
+ else:
161
+ pref = '|-'
162
+ ret += f' {pref} {k: >19}: {roi[k]}\n'
163
+
164
+ # plot mesh keys
165
+ key_list_mesh = []
166
+ for k, d in mesh.items():
167
+ if d is not None and k != 'name' and k != 'subject_id':
168
+ key_list_mesh.append(k)
169
+ for k in key_list_mesh:
170
+ if k == key_list_mesh[-1]:
171
+ pref = '└-'
172
+ else:
173
+ pref = '|-'
174
+ ret += f' {pref} {k: >19}: {mesh[k]}\n'
175
+
176
+ ret += "\n"
177
+ ret += '\n----------------------------Experiments-------------------------\n'
178
+ if not self.exp:
179
+ ret += 'None\n'
180
+ else:
181
+ for idx_exp, exp in self.exp.items():
182
+ ret += f"|- Exp name: {idx_exp}\n"
183
+ key_list_exp = []
184
+ for k, d in exp.items():
185
+ if d is not None and k != 'name' and k != 'subject_id':
186
+ key_list_exp.append(k)
187
+ for k in key_list_exp:
188
+ if k == key_list_exp[-1]:
189
+ pref = '└-'
190
+ else:
191
+ pref = '|-'
192
+ ret += f' {pref} {k: >19}: {exp[k]}\n'
193
+ return ret
194
+
195
+ def add_mesh_info(self, mesh_dict):
196
+ """
197
+ Adding filename information of the mesh to the subject object (multiple filenames possible).
198
+
199
+ Parameters
200
+ ----------
201
+ mesh_dict : dict or list of dict
202
+ Dictionary containing the mesh information.
203
+
204
+ Notes
205
+ -----
206
+ **Adds Attributes**
207
+
208
+ Subject.mesh : list of dict
209
+ Dictionaries containing the mesh information.
210
+ """
211
+ if type(mesh_dict) is list:
212
+ for mesh in mesh_dict:
213
+ self.mesh.append(mesh)
214
+
215
+ else:
216
+ self.mesh = mesh_dict
217
+
218
+ def add_roi_info(self, roi_dict):
219
+ """
220
+ Adding ROI (surface) information of the mesh with mesh_index to the subject object (multiple ROIs possible).
221
+
222
+ Parameters
223
+ ----------
224
+ roi_dict : dict of dict or list of dict
225
+ Dictionary containing the ROI information of the mesh with mesh_index ``[mesh_idx][roi_idx]``.
226
+
227
+ Notes
228
+ -----
229
+ **Adds Attributes**
230
+
231
+ Subject.mesh[mesh_index].roi : list of dict
232
+ Dictionaries containing ROI information.
233
+ """
234
+ for mesh_idx in roi_dict.keys():
235
+ self.roi[mesh_idx] = dict()
236
+
237
+ for roi_idx in roi_dict[mesh_idx].keys():
238
+ self.roi[mesh_idx][roi_idx] = roi_dict[mesh_idx][roi_idx]
239
+
240
+ def add_plotsettings(self, ps_dict):
241
+ """
242
+ Adding ROI information to the subject object (multiple ROIs possible).
243
+
244
+ Parameters
245
+ ----------
246
+ ps_dict : dict or list of dict
247
+ Dictionary containing plot settings of the subject.
248
+
249
+ Notes
250
+ -----
251
+ **Adds Attributes**
252
+
253
+ Subject.ps : list of dict
254
+ Dictionary containing plot settings of the subject.
255
+ """
256
+ if type(ps_dict) is not list:
257
+ ps_dict = [ps_dict]
258
+
259
+ for ps in ps_dict:
260
+ self.ps.append(ps)
261
+
262
+ def add_mri_info(self, mri_dict):
263
+ """
264
+ Adding MRI information to the subject object (multiple MRIs possible).
265
+
266
+ Parameters
267
+ ----------
268
+ mri_dict : dict or list of dict
269
+ Dictionary containing the MRI information of the subject.
270
+
271
+ Notes
272
+ -----
273
+ **Adds Attributes**
274
+
275
+ Subject.mri : list of dict
276
+ Dictionary containing the MRI information of the subject.
277
+ """
278
+ if type(mri_dict) is not list:
279
+ mri_dict = [mri_dict]
280
+
281
+ for mri in mri_dict:
282
+ self.mri.append(mri)
283
+
284
+ @staticmethod
285
+ def _prep_fn(val):
286
+ if type(val) is dict:
287
+ pass
288
+ elif type(val) is not list:
289
+ val = [val]
290
+ for j in range(len(val)):
291
+ _ = check_file_and_format(val[j])
292
+ return val
293
+
294
+ def add_experiment_info(self, exp_dict):
295
+ """
296
+ Adding information about a particular experiment.
297
+
298
+ Parameters
299
+ ----------
300
+ exp_dict : dict of dict or list of dict
301
+ Dictionary containing information about the experiment.
302
+
303
+ Notes
304
+ -----
305
+ **Adds Attributes**
306
+
307
+ exp : list of dict
308
+ Dictionary containing information about the experiment.
309
+ """
310
+ fns = ['fn_data', 'fn_coil', 'fn_mri_nii']
311
+ if exp_dict is None:
312
+ return
313
+
314
+ # if type(exp_dict) is not list:
315
+ # exp_dict = [exp_dict
316
+ # check if files and folder exist and convert to list
317
+ for i in exp_dict.keys():
318
+
319
+ # # fn_tms_nav
320
+ # if type(exp_dict[i]['fn_tms_nav']) is not list:
321
+ # exp_dict[i]['fn_tms_nav'] = [exp_dict[i]['fn_tms_nav']]
322
+ # for j in range(len(exp_dict[i]['fn_tms_nav'])):
323
+ # _ = check_file_and_format(exp_dict[i]['fn_tms_nav'][j])
324
+
325
+ # fn_data
326
+ for fn in fns:
327
+ try:
328
+ exp_dict[i][fn] = self._prep_fn(exp_dict[i][fn])
329
+ except KeyError:
330
+ pass
331
+
332
+ # new subject files have 'cond' as dictionary
333
+ if 'cond' in exp_dict[i].keys():
334
+ if isinstance(exp_dict[i]['cond'], dict):
335
+ for cond_name in exp_dict[i]['cond'].keys():
336
+ for fn in fns:
337
+ try:
338
+ exp_dict[i]['cond'][cond_name][fn] = self._prep_fn(exp_dict[i]['cond'][cond_name][fn])
339
+ except KeyError:
340
+ pass
341
+ else:
342
+ exp_dict[i]['cond'] = [[""]]
343
+
344
+ for exp in exp_dict:
345
+ self.exp[exp] = exp_dict[exp]
346
+
347
+
348
+ def fill_from_dict(obj, d):
349
+ """
350
+ Set all attributes from d in obj.
351
+
352
+ Parameters
353
+ ----------
354
+ obj : pynibs.Mesh or pynibs.roi.ROI
355
+ Object to fill with ``d``.
356
+ d : dict
357
+ Dictionary containing the attributes to set.
358
+
359
+ Returns
360
+ -------
361
+ obj : pynibs.Mesh or pynibs.ROI
362
+ Object with attributes set from ``d``.
363
+ """
364
+ for key, value in d.items():
365
+ if not hasattr(obj, f"{key}"):
366
+ warnings.warn(f"{key} not existing.")
367
+ setattr(obj, key, value)
368
+
369
+ return obj
370
+
371
+
372
+ def save_subject(subject_id, subject_folder, fname, mri_dict=None, mesh_dict=None, roi_dict=None,
373
+ exp_dict=None, ps_dict=None, **kwargs):
374
+ """
375
+ Saves subject information in .pkl or .hdf5 format (preferred)
376
+
377
+ Parameters
378
+ ----------
379
+ subject_id : str
380
+ ID of subject.
381
+ subject_folder : str
382
+ Subject folder
383
+ fname : str
384
+ Filename with .hdf5 or .pkl extension (incl. path).
385
+ mri_dict : list of dict, optional
386
+ MRI info.
387
+ mesh_dict : list of dict, optional
388
+ Mesh info.
389
+ roi_dict : list of list of dict, optional
390
+ Mesh info.
391
+ exp_dict : list of dict, optional
392
+ Experiment info.
393
+ ps_dict : list of dict, optional
394
+ Plot-settings info.
395
+ kwargs : str or np.ndarray
396
+ Additional information saved in the parent folder of the .hdf5 file.
397
+
398
+ Returns
399
+ -------
400
+ <File> : .hdf5 file
401
+ Subject information
402
+ """
403
+ filetype = os.path.splitext(fname)[1]
404
+
405
+ if filetype == ".hdf5":
406
+ if os.path.exists(fname):
407
+ os.remove(fname)
408
+
409
+ save_subject_hdf5(subject_id=subject_id,
410
+ subject_folder=subject_folder,
411
+ fname=fname,
412
+ mri_dict=mri_dict,
413
+ mesh_dict=mesh_dict,
414
+ roi_dict=roi_dict,
415
+ exp_dict=exp_dict,
416
+ ps_dict=ps_dict,
417
+ **kwargs)
418
+
419
+ elif filetype == ".pkl":
420
+ raise NotImplementedError
421
+ #
422
+ # # create and initialize subject
423
+ # subject = Subject(subject_id=subject_id)
424
+ #
425
+ # # add mri information
426
+ # subject.add_mri_info(mri_dict=mri_dict)
427
+ #
428
+ # # add mesh information
429
+ # subject.add_mesh_info(mesh_dict=mesh_dict)
430
+ #
431
+ # # add roi info to mesh
432
+ # subject.add_roi_info(roi_dict=roi_dict)
433
+ #
434
+ # # add experiment info
435
+ # subject.add_experiment_info(exp_dict=exp_dict)
436
+ #
437
+ # # add plotsettings
438
+ # subject.add_plotsettings(ps_dict=ps_dict)
439
+ #
440
+ # save_subject_pkl(sobj=subject, fname=fname)
441
+
442
+
443
+ def save_subject_pkl(sobj, fname):
444
+ """
445
+ Saving subject object as pickle file.
446
+
447
+ Parameters
448
+ ----------
449
+ sobj: object
450
+ Subject object to save
451
+ fname: str
452
+ Filename with .pkl extension
453
+
454
+ Returns
455
+ -------
456
+ <File> : .pkl file
457
+ Subject object instance
458
+ """
459
+
460
+ if type(fname) is list:
461
+ fname = fname[0]
462
+
463
+ with open(fname, 'wb') as output:
464
+ pickle.dump(sobj, output, -1)
465
+
466
+
467
+ def save_subject_hdf5(subject_id, subject_folder, fname, mri_dict=None, mesh_dict=None, roi_dict=None,
468
+ exp_dict=None, ps_dict=None, overwrite=True, check_file_exist=False, verbose=False, **kwargs):
469
+ """
470
+ Saving subject information in hdf5 file.
471
+
472
+ Parameters
473
+ ----------
474
+ subject_id : str
475
+ ID of subject.
476
+ subject_folder : str
477
+ Subject folder.
478
+ fname : str
479
+ Filename with .hdf5 extension (incl. path).
480
+ mri_dict : list of dict, optional
481
+ MRI info.
482
+ mesh_dict : list of dict, optional
483
+ Mesh info.
484
+ roi_dict : list of list of dict, optional
485
+ Mesh info.
486
+ exp_dict : list of dict or dict of dict, optional
487
+ Experiment info.
488
+ ps_dict : list of dict, optional, default:None
489
+ Plot-settings info.
490
+ overwrite : bool
491
+ Overwrites existing .hdf5 file.
492
+ check_file_exist : bool
493
+ Hide warnings.
494
+ verbose : bool
495
+ Print information about meshes and ROIs.
496
+ kwargs : str or np.ndarray
497
+ Additional information saved in the parent folder of the .hdf5 file.
498
+
499
+ Returns
500
+ -------
501
+ <File> : .hdf5 file
502
+ Subject information.
503
+ """
504
+ assert fname.endswith('.hdf5')
505
+
506
+ if overwrite and os.path.exists(fname):
507
+ os.remove(fname)
508
+
509
+ with h5py.File(fname, 'a') as f:
510
+ f["subject_id"] = np.array(subject_id).astype("|S")
511
+ f["subject_folder"] = np.array(subject_folder).astype("|S")
512
+
513
+ if mri_dict is not None:
514
+ if isinstance(mri_dict, list):
515
+ mri_dict = {i: mri_dict[i] for i in range(len(mri_dict))}
516
+
517
+ for i in mri_dict.keys():
518
+ pynibs.write_dict_to_hdf5(fn_hdf5=fname, data=mri_dict[i], folder=f"mri/{i}", check_file_exist=True)
519
+
520
+ if mesh_dict is not None:
521
+ if isinstance(mesh_dict, list):
522
+ mesh_dict = {i: mesh_dict[i] for i in range(len(mesh_dict))}
523
+
524
+ for mesh_name, mesh_dict in mesh_dict.items():
525
+ mesh = pynibs.Mesh(mesh_name=mesh_name, subject_id=subject_id, subject_folder=subject_folder)
526
+ mesh.fill_defaults(mesh_dict['approach'])
527
+ mesh = fill_from_dict(mesh, mesh_dict)
528
+ mesh.write_to_hdf5(fn_hdf5=fname, check_file_exist=check_file_exist, verbose=verbose)
529
+
530
+ if roi_dict is not None:
531
+ for mesh_name in roi_dict.keys():
532
+ for roi_name, roi_dict_i in roi_dict[mesh_name].items():
533
+ roi = pynibs.ROI(subject_id=subject_id, roi_name=roi_name, mesh_name=mesh_name)
534
+ roi = fill_from_dict(roi, roi_dict_i)
535
+ roi.write_to_hdf5(fn_hdf5=fname, check_file_exist=check_file_exist, verbose=verbose)
536
+
537
+ if exp_dict is not None:
538
+ if isinstance(exp_dict, list):
539
+ exp_dict = {i: exp_dict[i] for i in range(len(exp_dict))}
540
+ for i in exp_dict.keys():
541
+ pynibs.write_dict_to_hdf5(fn_hdf5=fname, data=exp_dict[i], folder=f"exp/{i}",
542
+ check_file_exist=check_file_exist)
543
+
544
+ if ps_dict is not None:
545
+ for i in range(len(ps_dict)):
546
+ pynibs.write_dict_to_hdf5(fn_hdf5=fname, data=ps_dict[i], folder=f"ps/{i}",
547
+ check_file_exist=check_file_exist)
548
+
549
+ with h5py.File(fname, 'a') as f:
550
+ for key, value in kwargs.items():
551
+ try:
552
+ del f[key]
553
+ except KeyError:
554
+ pass
555
+ f.create_dataset(name=key, data=value)
556
+
557
+
558
+ def load_subject_hdf5(fname):
559
+ """
560
+ Loading subject information from .hdf5 file and returning subject object.
561
+
562
+ Parameters
563
+ ----------
564
+ fname : str
565
+ Filename with .hdf5 extension (incl. path).
566
+
567
+ Returns
568
+ -------
569
+ subject : pynibs.subject.Subject
570
+ The Subject object.
571
+ """
572
+ with h5py.File(fname, 'r') as f:
573
+ subject_id = str(f["subject_id"][()].astype(str))
574
+ subject_folder = str(f["subject_folder"][()].astype(str))
575
+
576
+ # create and initialize subject
577
+ subject = Subject(subject_id=subject_id, subject_folder=subject_folder)
578
+
579
+ # add mri information
580
+ try:
581
+ mri_keys = f["mri"].keys()
582
+ mri = []
583
+
584
+ for key in mri_keys:
585
+ mri.append(pynibs.read_dict_from_hdf5(fn_hdf5=fname, folder=f"mri/{key}"))
586
+
587
+ except KeyError:
588
+ mri = None
589
+
590
+ subject.add_mri_info(mri_dict=mri)
591
+
592
+ # add mesh information
593
+ try:
594
+ mesh_keys = f["mesh"].keys()
595
+ mesh = {}
596
+
597
+ for key in mesh_keys:
598
+ mesh[key] = pynibs.read_dict_from_hdf5(fn_hdf5=fname, folder=f"mesh/{key}")
599
+
600
+ except KeyError:
601
+ mesh = None
602
+
603
+ subject.add_mesh_info(mesh_dict=mesh)
604
+
605
+ # add roi info
606
+ try:
607
+ mesh_idx_keys = f["roi"].keys()
608
+ roi = dict()
609
+
610
+ for mesh_idx in mesh_idx_keys:
611
+ try:
612
+ roi_idx_keys = f[f"roi/{mesh_idx}"].keys()
613
+ roi[mesh_idx] = dict()
614
+
615
+ for roi_idx in roi_idx_keys:
616
+ roi[mesh_idx][roi_idx] = pynibs.read_dict_from_hdf5(fn_hdf5=fname,
617
+ folder=f"roi/{mesh_idx}/{roi_idx}")
618
+
619
+ except KeyError:
620
+ roi[mesh_idx] = None
621
+
622
+ subject.add_roi_info(roi_dict=roi)
623
+
624
+ except KeyError:
625
+ pass
626
+
627
+ # add experiment information
628
+ try:
629
+ exp_keys = f["exp"].keys()
630
+ exp = {}
631
+
632
+ for key in exp_keys:
633
+ exp[key] = pynibs.read_dict_from_hdf5(fn_hdf5=fname, folder=f"exp/{key}")
634
+
635
+ except KeyError:
636
+ exp = None
637
+
638
+ subject.add_experiment_info(exp_dict=exp)
639
+
640
+ # add plotsettings information
641
+ try:
642
+ ps_keys = f["ps"].keys()
643
+ ps = []
644
+
645
+ for key in ps_keys:
646
+ ps.append(pynibs.read_dict_from_hdf5(fn_hdf5=fname, folder=f"ps/{key}"))
647
+
648
+ except KeyError:
649
+ ps = None
650
+
651
+ subject.add_plotsettings(ps_dict=ps)
652
+
653
+ return subject
654
+
655
+
656
+ def load_subject(fname, filetype=None):
657
+ """
658
+ Wrapper for pkl and hdf5 subject loader
659
+
660
+ Parameters
661
+ ----------
662
+ fname: str
663
+ endswith('.pkl') | endswith('.hdf5').
664
+ filetype: str
665
+ Explicitly set file version.
666
+
667
+ Returns
668
+ -------
669
+ subject : pynibs.subject.Subject
670
+ Loaded Subject object.
671
+ """
672
+ # explicit set fname type
673
+ if filetype:
674
+ if filetype.lower().endswith("hdf5"):
675
+ filetype = "hdf5"
676
+ elif filetype.lower().endswith("pkl"):
677
+ filetype = "pkl"
678
+ else:
679
+ raise NotImplementedError(f"{filetype} unknown.")
680
+
681
+ # determine fname type from file-ending
682
+ else:
683
+ if fname.lower().endswith("hdf5"):
684
+ filetype = "hdf5"
685
+ elif fname.lower().endswith("pkl"):
686
+ filetype = "pkl"
687
+ else:
688
+ raise NotImplementedError(f"{fname} type unknown.")
689
+
690
+ # load file using correct load function
691
+ if filetype == "hdf5":
692
+ return load_subject_hdf5(fname)
693
+ elif filetype == "pkl":
694
+ return load_subject_pkl(fname)
695
+
696
+
697
+ def load_subject_pkl(fname):
698
+ """
699
+ Loading subject object from .pkl file.
700
+
701
+ Parameters
702
+ ----------
703
+ fname : str
704
+ Filename with .pkl extension.
705
+
706
+ Returns
707
+ -------
708
+ subject : pynibs.subject.Subject
709
+ Loaded Subject object.
710
+ """
711
+ try:
712
+ with open(fname, 'rb') as f:
713
+ return pickle.load(f)
714
+
715
+ except UnicodeDecodeError:
716
+ print(".pkl file version does not match python version... recreating subject object")
717
+ fn_scipt = os.path.join(os.path.split(fname)[0],
718
+ "create_subject_" +
719
+ os.path.splitext(os.path.split(fname)[1])[0] + ".py")
720
+ pynibs.bash_call("python {}".format(fn_scipt))
721
+
722
+ with open(fname, 'rb') as f:
723
+ return pickle.load(f)
724
+
725
+
726
+ def check_file_and_format(fname):
727
+ """
728
+ Checking existence of file and transforming to list if necessary.
729
+
730
+ Parameters
731
+ ----------
732
+ fname: str or list of str
733
+ Filename(s) to check.
734
+
735
+ Returns
736
+ -------
737
+ fname: list of str
738
+ Checked filename(s) as list.
739
+ """
740
+ if type(fname) is not list:
741
+ fname = [fname]
742
+
743
+ for fn in fname:
744
+ if not (os.path.exists(fn)):
745
+ Exception('File/Folder {} does not exist!'.format(fn))
746
+
747
+ return fname
748
+
749
+
750
+ def create_plot_settings_dict(plotfunction_type):
751
+ """
752
+ Creates a dictionary with default plotsettings.
753
+
754
+ Parameters
755
+ ----------
756
+ plotfunction_type : str
757
+ Plot function the dictionary is generated for:
758
+
759
+ * 'surface_vector_plot'
760
+ * 'surface_vector_plot_vtu'
761
+ * 'volume_plot'
762
+ * 'volume_plot_vtu'
763
+
764
+ Returns
765
+ -------
766
+ ps : dict
767
+ Dictionary containing the plotsettings.
768
+ axes : bool
769
+ Show orientation axes.
770
+ background_color : nparray
771
+ (1m 3) Set background color of exported image RGB (0...1).
772
+ calculator : str
773
+ Format string with placeholder of the calculator expression the quantity to plot is modified with,
774
+ e.g.: "{}^5".
775
+ clip_coords : nparray of float
776
+ (N_clips, 3) Coordinates of clip surface origins (x,y,z).
777
+ clip_normals : nparray of float
778
+ (N_clips, 3) Surface normals of clip surfaces pointing in the direction where the volume is kept for
779
+ clip_type = ['clip' ...] (x,y,z).
780
+ clip_type : list of str
781
+ Type of clipping:
782
+
783
+ * 'clip': cut geometry but keep volume behind
784
+ * 'slice': cut geometry and keep only the slice
785
+ coil_dipole_scaling : list [1 x 2]
786
+ Specify the scaling type of the dipoles (2 entries):
787
+ ``coil_dipole_scaling[0]``:
788
+
789
+ * 'uniform': uniform scaling, i.e. all dipoles have the same size
790
+ + 'scaled': size scaled according to dipole magnitude
791
+
792
+ ``coil_dipole_scaling[1]``:
793
+
794
+ * scalar scale parameter of dipole size
795
+ coil_dipole_color : str or list
796
+ Color of the dipoles; either str to specify colormap (e.g. 'jet') or list of RGB values [1 x 3] (0...1).
797
+ coil_axes : bool, default: True
798
+ Plot coil axes visualizing the principle direction and orientation of the coil.
799
+ colorbar_label : str
800
+ Label of plotted data close to colorbar.
801
+ colorbar_position : list of float
802
+ (1, 2) Position of colorbar (lower left corner) 0...1 [x_pos, y_pos].
803
+ colorbar_orientation : str
804
+ Orientation of colorbar (``'Vertical'``, ``'Horizontal'``).
805
+ colorbar_aspectratio : int
806
+ Aspectratio of colorbar (higher values make it thicker).
807
+ colorbar_titlefontsize : float
808
+ Fontsize of colorbar title.
809
+ colorbar_labelfontsize : float
810
+ Fontsize of colorbar labels (numbers).
811
+ colorbar_labelformat : str
812
+ Format of colorbar labels (e.g.: '%-#6.3g').
813
+ colorbar_numberoflabels : int
814
+ maximum number of colorbar labels.
815
+ colorbar_labelcolor : list of float
816
+ (1, 3) Color of colorbar labels in RGB (0...1).
817
+ colormap : str or nparray
818
+ If nparray [1 x 4*N]: custom colormap providing data and corresponding RGB values
819
+
820
+ .. math::
821
+ \\begin{bmatrix}
822
+ data_{1} & R_1 & G_1 & B_1 \\\\
823
+ data_{2} & R_2 & G_2 & B_2 \\\\
824
+ ... & ... & ... & ... \\\\
825
+ data_{N} & R_N & G_N & B_N \\\\
826
+ \\end{bmatrix}
827
+
828
+ if str: colormap of plotted data chosen from included presets:
829
+
830
+ * 'Cool to Warm',
831
+ * 'Cool to Warm (Extended)',
832
+ * 'Blue to Red Rainbow',
833
+ * 'X Ray',
834
+ * 'Grayscale',
835
+ * 'jet',
836
+ * 'hsv',
837
+ * 'erdc_iceFire_L',
838
+ * 'Plasma (matplotlib)',
839
+ * 'Viridis (matplotlib)',
840
+ * 'gray_Matlab',
841
+ * 'Spectral_lowBlue',
842
+ + 'BuRd'
843
+ * 'Rainbow Blended White'
844
+ * 'b2rcw'
845
+ colormap_categories : bool
846
+ Use categorized (discrete) colormap.
847
+ datarange : list
848
+ (1, 2) Minimum and Maximum of plotted datarange [MIN, MAX] (default: automatic).
849
+ domain_IDs : int or list of int
850
+ Domain IDs
851
+ surface plot: Index of surface where the data is plotted on (Default: 0)
852
+ volume plot: Specify the domains IDs to show in plot (default: all) Attention! Has to be included in the
853
+ dataset under the name 'tissue'! e.g. for SimNIBS:
854
+
855
+ * 1 -> white matter (WM)
856
+ * 2 -> grey matter (GM)
857
+ * 3 -> cerebrospinal fluid (CSF)
858
+ * 4 -> skull
859
+ * 5 -> skin
860
+ domain_label : str
861
+ Label of the dataset which contains the domain IDs (default: 'tissue_type').
862
+ edges : BOOL
863
+ Show edges of mesh.
864
+ fname_in : str or list of str
865
+ Filenames of input files, 2 possibilities:
866
+
867
+ * .xdmf-file: filename of .xmdf (needs the corresponding .hdf5 file(s) in the same folder)
868
+ * .hdf5-file(s): filename(s) of .hdf5 file(s) containing the data and the geometry. The data can be provided
869
+ in the first hdf5 file and the geometry can be provided in the second file. However, both can be also
870
+ provided in a single hdf5 file.
871
+ fname_png : str
872
+ Name of output .png file (incl. path).
873
+ fname_vtu_volume : str
874
+ Name of .vtu volume file containing volume data (incl. path).
875
+ fname_vtu_surface : str
876
+ Name of .vtu surface file containing surface data (incl. path) (to distinguish tissues).
877
+ fname_vtu_coil : str, optional
878
+ Name of coil .vtu file (incl. path).
879
+ info : str
880
+ Information about the plot the settings belong to.
881
+ interpolate : bool
882
+ Interpolate data for visual smoothness.
883
+ NanColor : list of float
884
+ (3) RGB color values for "Not a Number" values (range 0 ... 1).
885
+ opacitymap : np.ndarray
886
+ Points defining the piecewise linear opacity transfer function (transparency) (default: no transparency)
887
+ connecting data values with opacity (alpha) values ranging from 0 (max. transparency) to 1 (no transparency).
888
+
889
+ .. math::
890
+ \\begin{bmatrix}
891
+ data_{1} & opac_1 & 0.5 & 0 \\\\
892
+ data_{2} & opac_2 & 0.5 & 0 \\\\
893
+ ... & ... & ... & ...\\\\
894
+ data_{N} & opac_N & 0.5 & 0 \\\\
895
+ \\end{bmatrix}
896
+ plot_function : str
897
+ Function the plot is generated with:
898
+
899
+ - 'surface_vector_plot'
900
+ - 'surface_vector_plot_vtu'
901
+ - 'volume_plot'
902
+ - 'volume_plot_vtu'
903
+ png_resolution : float
904
+ Resolution parameter of output image (1...5).
905
+ quantity : str
906
+ Label of magnitude dataset to plot.
907
+ surface_color : np.ndarray
908
+ (1, 3) Color of brain surface in RGB (0...1) for better visibility of tissue borders.
909
+ surface_smoothing : bool
910
+ Smooth the plotted surface (True/False).
911
+ show_coil : bool, default: True
912
+ show coil if present in dataset as block termed 'coil'.
913
+ vcolor : np.ndarray of float
914
+ (N_vecs, 3) Array containing the RGB values between 0...1 of the vector groups in dataset to plot.
915
+ vector_mode : dict
916
+ Key determines the type how many vectors are shown:
917
+
918
+ - 'All Points'
919
+ - 'Every Nth Point'
920
+ - 'Uniform Spatial Distribution'
921
+
922
+ Value (int) is the corresponding number of vectors:
923
+
924
+ - 'All Points' (not set)
925
+ - 'Every Nth Point' (every Nth vector is shown in the grid)
926
+ - 'Uniform Spatial Distribution' (not set)
927
+ view : list
928
+ Camera position and angle in 3D space:
929
+ ``[[3 x CameraPosition], [3 x CameraFocalPoint], [3 x CameraViewUp], 1 x CameraParallelScale]``.
930
+ viewsize : nparray [1 x 2]
931
+ Set size of exported image in pixel [width x height] will be extra scaled by parameter png_resolution.
932
+ vlabels : list of str
933
+ Labels of vector datasets to plot (other present datasets are ignored).
934
+ vscales : list of float
935
+ Scale parameters of vector groups to plot.
936
+ vscale_mode : list of str [N_vecs x 1]
937
+ List containing the type of vector scaling:
938
+
939
+ - 'off': all vectors are normalized
940
+ - 'vector': vectors are scaled according to their magnitudeeee
941
+ """
942
+ if plotfunction_type not in ['surface_vector_plot', 'surface_vector_plot_vtu', 'volume_plot', 'volume_plot_vtu']:
943
+ raise Exception('plotfunction_type not set correctly, specify either [\'surface_vector_plot\', \
944
+ \'surface_vector_plot_vtu\', \'volume_plot\', \'volume_plot_vtu\']')
945
+
946
+ ps = dict()
947
+ ps['info'] = []
948
+ ps['plot_function'] = plotfunction_type
949
+ ps['fname_png'] = []
950
+ ps['png_resolution'] = 1
951
+ ps['quantity'] = None
952
+ ps['datarange'] = None
953
+ ps['colorbar_label'] = ''
954
+ ps['colorbar_position'] = []
955
+ ps['colorbar_orientation'] = 'Vertical'
956
+ ps['colorbar_aspectratio'] = 20
957
+ ps['colorbar_titlefontsize'] = 7
958
+ ps['colorbar_labelfontsize'] = 7
959
+ ps['colorbar_labelformat'] = '%-#6.3g'
960
+ ps['colorbar_numberoflabels'] = 5
961
+ ps['colorbar_labelcolor'] = [0, 0, 0]
962
+ ps['colorbar_font'] = 'Arial'
963
+ ps['view'] = 0
964
+ ps['interpolate'] = False
965
+ ps['edges'] = False
966
+ ps['axes'] = True
967
+ ps['colormap'] = "jet"
968
+ ps['colormap_categories'] = False
969
+ ps['opacitymap'] = None
970
+ ps['background_color'] = [1, 1, 1]
971
+ ps['viewsize'] = [800, 800]
972
+ ps['NanColor'] = [1, 1, 1]
973
+ ps["surface_smoothing"] = True
974
+ ps["calculator"] = None
975
+
976
+ # filename of input files
977
+ if plotfunction_type in ['surface_vector_plot', 'surface_vector_plot_vtu', 'volume_plot']:
978
+ ps['fname_in'] = []
979
+
980
+ elif plotfunction_type in ['volume_plot_vtu']:
981
+ ps['fname_vtu_volume'] = []
982
+ ps['fname_vtu_surface'] = []
983
+ ps['fname_vtu_coil'] = []
984
+
985
+ # new features in .xdmf plot functions
986
+ if plotfunction_type in ['surface_vector_plot', 'volume_plot', 'volume_plot_vtu']:
987
+ ps['domain_label'] = 'tissue_type'
988
+ ps['domain_IDs'] = 0
989
+ ps['show_coil'] = True
990
+ ps['coil_dipole_scaling'] = ['uniform', 1]
991
+ ps['coil_dipole_color'] = 'jet'
992
+ ps['coil_axes'] = True
993
+
994
+ # surface specific properties
995
+ if plotfunction_type in ['surface_vector_plot', 'surface_vector_plot_vtu']:
996
+ ps['vlabels'] = ''
997
+ ps['vscales'] = np.array([1])
998
+ ps['vscale_mode'] = 'vector'
999
+ ps['vector_mode'] = {'All Points': 0}
1000
+ ps['vcolor'] = np.array([1.0, 0.0, 0.0])
1001
+
1002
+ # volume specific properties
1003
+ if plotfunction_type in ['volume_plot', 'volume_plot_vtu']:
1004
+ ps['clip_coords'] = np.array([])
1005
+ ps['clip_normals'] = np.array([])
1006
+ ps['clip_type'] = []
1007
+ ps['surface_color'] = [1, 1, 1]
1008
+
1009
+ return ps