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
@@ -1,5 +1,7 @@
1
1
  import os
2
2
  import copy
3
+ import h5py
4
+ import pygpc
3
5
  import pynibs
4
6
  import numpy as np
5
7
  import _pickle as pickle
@@ -206,7 +208,7 @@ def calc_e_threshold(layerid, theta, gradient=None, mep=None, neuronmodel="sensi
206
208
  params[:, 2] = np.repeat(mep_norm, theta.shape[1])
207
209
 
208
210
  idx = np.arange(params.shape[0])
209
- idx_chunked = pynibs.compute_chunks(list(idx), multiprocessing.cpu_count())
211
+ idx_chunked = pynibs.util.utils.compute_chunks(list(idx), multiprocessing.cpu_count())
210
212
 
211
213
  pool = multiprocessing.Pool(multiprocessing.cpu_count())
212
214
  workhorse_partial = partial(workhorse_interp, interp=interp, params=params)
@@ -215,6 +217,7 @@ def calc_e_threshold(layerid, theta, gradient=None, mep=None, neuronmodel="sensi
215
217
  pool.close()
216
218
  pool.join()
217
219
 
220
+
218
221
  else:
219
222
  raise NotImplementedError
220
223
 
@@ -247,6 +250,7 @@ def calc_e_effective(e, layerid, theta, gradient=None, neuronmodel="sensitivity_
247
250
  - "threshold_binary": assign e-field a binary value to predict MEPs
248
251
  (False -> below threshold, True, above threshold).
249
252
  - "IOcurve": subtract value read from precomputed neuron IO curve from electric field.
253
+
250
254
  mep : np.ndarray of float [N_stim], optional
251
255
  MEP data (required in case of "IOcurve" approach (neuronmodel)).
252
256
  waveform : str, default: 'biphasic'
@@ -254,6 +258,7 @@ def calc_e_effective(e, layerid, theta, gradient=None, neuronmodel="sensitivity_
254
258
 
255
259
  - "monophasic"
256
260
  - "biphasic"
261
+
257
262
  e_thresh_subject : float, optional
258
263
  Subject specific stimulation threshold in V/m. Typically, between 60 ... 80 V/m.
259
264
  This is not used for sensitivity_weighting and
@@ -263,14 +268,22 @@ def calc_e_effective(e, layerid, theta, gradient=None, neuronmodel="sensitivity_
263
268
  e_eff : np.ndarray
264
269
  Effective electric field (matrix) [N_stim x N_ele] the regression analysis can be performed with.
265
270
  """
271
+ e_thres = None
272
+ q = None
266
273
  # determine sensitivity map
267
- e_thres = calc_e_threshold(layerid=layerid,
268
- theta=theta,
269
- gradient=gradient,
270
- neuronmodel=neuronmodel,
271
- mep=mep,
272
- waveform=waveform,
273
- e_thresh_subject=e_thresh_subject)
274
+ if neuronmodel in ["threshold_subtract", "threshold_binary", "sensitivity_weighting", "cosine", "IOcurve"]:
275
+ e_thres = calc_e_threshold(layerid=layerid,
276
+ theta=theta,
277
+ gradient=gradient,
278
+ neuronmodel=neuronmodel,
279
+ mep=mep,
280
+ waveform=waveform,
281
+ e_thresh_subject=e_thresh_subject)
282
+ elif neuronmodel == "neuron_coupling":
283
+ q = calc_q_coupling_model(e_mag=e,
284
+ theta=theta,
285
+ gradient=gradient,
286
+ waveform=waveform)
274
287
 
275
288
  if neuronmodel == "threshold_subtract":
276
289
  e_eff = e - e_thres
@@ -278,7 +291,84 @@ def calc_e_effective(e, layerid, theta, gradient=None, neuronmodel="sensitivity_
278
291
  e_eff = e > e_thres
279
292
  elif neuronmodel in ["sensitivity_weighting", "cosine"]:
280
293
  e_eff = e / e_thres
294
+ elif neuronmodel == "neuron_coupling":
295
+ e_eff = q * e
281
296
  else:
282
297
  e_eff = np.zeros(e.shape)
283
298
 
284
299
  return e_eff
300
+
301
+ def calc_q_coupling_model(e_mag, theta, gradient, waveform="monophasic"):
302
+ """
303
+ Calculates the average dendritic current based of the neuron coupling model [1]. This current can the be
304
+ integrated over time, to yield a charge factor that is multiplied with the electric field to calculate
305
+ an effective electric field (e_eff).
306
+
307
+ [1] Miller, A., Knösche, T. R., & Weise, K. (2025). A coupling model of transcranial magnetic stimulation
308
+ activation of cortical cell populations. Brain Stimulation: Basic, Translational, and Clinical Research
309
+ in Neuromodulation, 18(1), 528-529.
310
+
311
+ Parameters
312
+ ----------
313
+ e : np.ndarray
314
+ (N_stim, N_ele) Electric field (matrix).
315
+ theta : np.ndarray
316
+ (N_stim, N_ele) Theta angle (matrix) of electric field with respect to surface normal.
317
+ gradient : np.ndarray, optional
318
+ (N_stim, N_ele) Electric field gradient (matrix) between layer 1 and layer 6. Optional, the neuron mean field
319
+ model is more accurate when provided.
320
+ waveform : str, default: 'biphasic'
321
+ Waveform of TMS pulse:
322
+
323
+ - "monophasic"
324
+ - "biphasic"
325
+
326
+ Returns
327
+ -------
328
+ charges : np.ndarray
329
+ electric charges (matrix) [N_stim x N_ele] that can be used as scaling factors for the regression analysis.
330
+ """
331
+
332
+ if waveform != 'biphasic':
333
+ raise NotImplementedError('only biphasic stimulation is implemented for the neuron-coupling model yet!')
334
+
335
+ # scaling factor for current (gpc was done in normalized current space)
336
+ i_scale = 5.148136e-9
337
+ t = np.linspace(0, 99.81, 500)
338
+
339
+ # read gpc session
340
+ fn_session = '/data/u_emueller_software/git/pynibs/pynibs/neuron/neuron_coupling_model.pkl'
341
+ session = pygpc.read_session(fname=fn_session)
342
+
343
+ with h5py.File(os.path.splitext(fn_session)[0] + ".hdf5", "r") as f:
344
+ coeffs = f["coeffs"][:]
345
+
346
+ # create grid object to transform from real to normalized coordinates [-1, 1]
347
+ #default values
348
+ # theta = 0 # angle of e-field [0, 180]°
349
+ # gradient = 0 # relative gradient of e-field [-20, 20] %/mm
350
+ # intensity = 250 # intensity of e-field [100, 400] V/m
351
+ fraction_nmda = 0.5 # fraction of nmda synapses [0.25, 0.75]
352
+ fraction_gaba_a = 0.95 # fraction of gaba_a synapses [0.9, 1.0]
353
+ fraction_ex = 0.6 # fraction of exc/ihn synapses [0.2, 0.8]
354
+
355
+ T_new = 30
356
+ dt_new = 0.01
357
+ t_new = np.arange(0, T_new, dt_new)
358
+
359
+ m, n = e_mag.shape
360
+ l = t_new.shape[0]
361
+ interp_current = np.zeros((m, n, l))
362
+ for i in range(n):
363
+ for j in range(m):
364
+ coords = np.array([[theta[i, j], gradient[i, j], e_mag[i, j], fraction_nmda, fraction_gaba_a, fraction_ex]])
365
+ grid = pygpc.RandomGrid(parameters_random=session.parameters_random, coords=coords)
366
+
367
+ # use gpc approximation to compute current
368
+ current = session.gpc[0].get_approximation(coeffs, grid.coords_norm) * i_scale
369
+ current = current.flatten()
370
+ t_new = np.arange(0, T_new, dt_new)
371
+ interp_current[i, j, :] = np.interp(t_new, t, current)
372
+
373
+ charges = interp_current.sum(axis=2) # integrate (sum) over time with assumption i(t=0) = 0
374
+ return charges
@@ -1,5 +1,15 @@
1
- """Routines to compute optimal sets of electric fields. """
1
+ """
2
+ Routines to compute optimal sets of electric fields, either by optimizing
3
+ - single-channel TMS coil placements, or
4
+ - input currents for multichannel TMS coil arrays.
5
+
6
+ Used in [1]_
7
+
8
+ .. [1] Numssen, O., Martin, C. W., Worbs, T., Thielscher, A., Weise, K., & Knösche, T. R. (2025).
9
+ Optimizing and assessing multichannel TMS focality. Cold Spring Harbor Laboratory.
10
+ `10.1101/2025.09.19.677136 <https://doi.org/10.1101/2025.09.19.677136>`_
11
+ """
2
12
  from .opt_mep import *
3
- from .optimization import *
13
+ from .coil_opt import *
4
14
  from .workhorses import *
5
15
  from .multichannel import *