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
@@ -0,0 +1,566 @@
1
+ import pynibs
2
+ import numpy as np
3
+ from scipy.special import logsumexp
4
+
5
+
6
+ class Workhorse(object):
7
+
8
+ def __init__(self, t, aMN, renshaw):
9
+ self.t = t
10
+ self.dt = t[1] - t[0]
11
+ self.aMN = aMN
12
+ self.renshaw = renshaw
13
+
14
+ def run(self, input):
15
+ """
16
+ Workhorse to run alpha motor neurons in parallel
17
+
18
+ Parameters
19
+ ----------
20
+ input: ndarray of float [n_t]
21
+ Input signal to alpha motor neurons
22
+
23
+ Returns
24
+ -------
25
+ alpha_motor_neurons: list of AlphaMotorNeuron instances
26
+ Alpha motor neurons with calculated membrane potential (Vm) and spike times (spike_times)
27
+ """
28
+
29
+ # iterate over each time step
30
+ feedback = np.zeros(len(self.t))
31
+ delay = round(self.renshaw.delay / self.dt)
32
+
33
+ for i, t_ in enumerate(self.t):
34
+ feedback[i] = self.aMN.step(input, feedback, t_, i)
35
+
36
+ if i + delay < len(self.t) - 1:
37
+ feedback[i + 1] = self.renshaw.step(feedback, t_, i) # TODO consider delay and length of signal
38
+
39
+ return self.aMN
40
+
41
+
42
+ class Model(object):
43
+ """
44
+ Spinal cord and Muscle model containing alpha motor neurons and Renshaw cells.
45
+ """
46
+
47
+ def __init__(self, T, dt, N_MU=100, fn_muaps=None, **kwargs):
48
+ """
49
+ Initialize spinal cord and Muscle model
50
+
51
+ Parameters
52
+ ----------
53
+ T : Total Time of obesrvation
54
+ dt : time step
55
+ N_MU: Number of Motor units
56
+ fn_muaps : Path to muap shapes
57
+ **kwars: constants of neurons
58
+ """
59
+
60
+ # setup parameters and state variables
61
+ self.N_MU = N_MU
62
+ self.T = T
63
+ self.dt = dt
64
+ self.t = np.arange(0, T + dt, dt)
65
+
66
+ # Neuron
67
+ self.Vm_thr_array = kwargs['n_thr'] + kwargs['m_thr'] * np.arange(self.N_MU) # thresholds of alpha MNs
68
+ self.t_refrac = kwargs['t_refrac']
69
+ self.tau_mem = kwargs['tau_mem']
70
+ self.Vm_rest = kwargs['Vm_rest']
71
+ self.Rm = kwargs['Rm']
72
+
73
+ # Renshaw cell (inhibitory)
74
+ self.slope_renshaw = kwargs.get('slope_renshaw')
75
+ self.Vm_thr_renshaw = kwargs.get('Vm_thr_renshaw')
76
+ self.Vm_rest_renshaw = kwargs.get('Vm_rest_renshaw')
77
+
78
+ # Acetyl synapse (excitatory)
79
+ self.tau_syn_decay_ac = kwargs.get('tau_syn_decay_ac')
80
+ self.tau_syn_rise_ac = kwargs.get('tau_syn_rise_ac')
81
+ self.tau_syn_decay_fast_ac = kwargs.get('tau_syn_decay_fast_ac')
82
+ self.tau_syn_decay_slow_ac = kwargs.get('tau_syn_decay_slow_ac')
83
+ self.a_syn_decay_fast_ac = kwargs.get('a_syn_decay_fast_ac')
84
+ self.a_syn_decay_slow_ac = kwargs.get('a_syn_decay_slow_ac')
85
+
86
+ # Glycin Synapse (inhibitory)
87
+ self.tau_syn_decay_gl = kwargs.get('tau_syn_decay_gl')
88
+ self.tau_syn_rise_gl = kwargs.get('tau_syn_rise_gl')
89
+ self.tau_syn_decay_fast_gl = kwargs.get('tau_syn_decay_fast_gl')
90
+ self.tau_syn_decay_slow_gl = kwargs.get('tau_syn_decay_slow_gl')
91
+ self.a_syn_decay_fast_gl = kwargs.get('a_syn_decay_fast_gl')
92
+ self.a_syn_decay_slow_gl = kwargs.get('a_syn_decay_slow_gl')
93
+
94
+ # Connectivity
95
+ self.connectivity_cortex_alpha = kwargs.get('connectivity_cortex_alpha')
96
+ self.connectivity_renshaw_alpha = kwargs.get('connectivity_renshaw_alpha')
97
+ self.connectivity_alpha_renshaw = kwargs.get('connectivity_alpha_renshaw')
98
+
99
+ # Load MUAPS
100
+ self.fn_muaps = fn_muaps
101
+ self.muaps = []
102
+ self.t_muap = []
103
+ self.muaps, t_muap = pynibs.load_muaps(self.fn_muaps)
104
+
105
+ # Workhorse Arrays
106
+ self.alpha_motor_neurons = []
107
+ self.renshaw_cells = []
108
+ self.motor_units = []
109
+
110
+ # Output Values
111
+ self.mep = None
112
+ self.mep_p2p = None
113
+
114
+ # Synapse (Cortex -> Alpha Motor Neurons)
115
+ acetyl_synapse_1 = Synapse(dt=self.dt, T=self.T,
116
+ tau_syn_decay_fast=self.tau_syn_decay_fast_ac,
117
+ tau_syn_decay_slow=self.tau_syn_decay_slow_ac,
118
+ tau_syn_rise=self.tau_syn_rise_ac,
119
+ a_syn_decay_slow=self.a_syn_decay_slow_ac,
120
+ a_syn_decay_fast=self.a_syn_decay_fast_ac,
121
+ connectivity=kwargs.get('connectivity_cortex_alpha'),
122
+ N=self.N_MU)
123
+
124
+ # Synapse (Alpha Motor Neurons -> Renshaw Cells)
125
+ acetyl_synapse_2 = Synapse(dt=self.dt, T=self.T,
126
+ tau_syn_decay_fast=self.tau_syn_decay_fast_ac,
127
+ tau_syn_decay_slow=self.tau_syn_decay_slow_ac,
128
+ tau_syn_rise=self.tau_syn_rise_ac,
129
+ a_syn_decay_slow=self.a_syn_decay_slow_ac,
130
+ a_syn_decay_fast=self.a_syn_decay_fast_ac,
131
+ connectivity=self.connectivity_alpha_renshaw,
132
+ N=self.N_MU)
133
+
134
+ # Synapse (Renshaw Cells -> Alpha Motor Neurons)
135
+ glycine_synapse = Synapse(dt=self.dt, T=self.T,
136
+ tau_syn_decay_fast=self.tau_syn_decay_fast_gl,
137
+ tau_syn_decay_slow=self.tau_syn_decay_slow_gl,
138
+ tau_syn_rise=self.tau_syn_rise_gl,
139
+ a_syn_decay_slow=self.a_syn_decay_slow_gl,
140
+ a_syn_decay_fast=self.a_syn_decay_fast_gl,
141
+ connectivity=self.connectivity_renshaw_alpha,
142
+ N=self.N_MU)
143
+
144
+ self.aMNs = AlphaMotorNeuron(tau_mem=self.tau_mem, Vm_thr=self.Vm_thr_array, Vm_rest=self.Vm_rest, Rm=self.Rm,
145
+ t_refrac=self.t_refrac, T=self.T, dt=self.dt,
146
+ synapses=[acetyl_synapse_1, glycine_synapse], N=self.N_MU)
147
+
148
+ self.renshaw = RenshawCell(tau_mem=self.tau_mem, Vm_thr=self.Vm_thr_renshaw, Vm_rest=self.Vm_rest, Rm=self.Rm,
149
+ T=self.T, dt=self.dt, delay=0,
150
+ synapses=[acetyl_synapse_2],
151
+ slope=self.slope_renshaw) # TODO own Vm_thr for optimisation
152
+
153
+ for i in range(self.N_MU):
154
+ self.motor_units.append(MotorUnit(muap=self.muaps[:, i], t=self.t))
155
+
156
+ def run(self, inp):
157
+ """
158
+ Starts the parallel processing.
159
+ Creates Model can all workhorse process
160
+
161
+ Parameters
162
+ ----------
163
+ inp: ndarray of float [n_t]
164
+ Input signal to alpha motor neurons
165
+ """
166
+
167
+ workhorse = Workhorse(self.t, self.aMNs, self.renshaw)
168
+ self.alpha_motor_neurons = workhorse.run(inp)
169
+
170
+ muscle = Muscle(self.alpha_motor_neurons, self.motor_units)
171
+ self.mep = muscle.get_mep()
172
+ self.mep_p2p = np.max(self.mep) - np.min(self.mep)
173
+
174
+
175
+ class Synapse(object):
176
+ """
177
+ Synapse (conductance-based)
178
+ """
179
+
180
+ def __init__(self, T, dt, N, tau_syn_decay=None, tau_syn_rise=None,
181
+ tau_syn_decay_fast=None, tau_syn_decay_slow=None, a_syn_decay_fast=None, a_syn_decay_slow=None,
182
+ connectivity=1):
183
+ """
184
+ Initializes Synapse instance
185
+
186
+ Parameters
187
+ ----------
188
+ T : float
189
+ Total simulation time in ms
190
+ dt : float
191
+ Time-step to solve DEQ
192
+ tau_syn_decay : float
193
+ Decay time constant of synapse in ms (set this parameter, if only one decay time constant is present and set
194
+ tau_syn_decay_fast=None, tau_syn_decay_slow=None)
195
+ tau_syn_rise : float
196
+ Rise time of synapse in ms
197
+ tau_syn_decay_fast : float
198
+ Decay time constant of synapse of fast component in ms
199
+ tau_syn_decay_slow : float
200
+ Decay time constant of synapse of slow component in ms
201
+ a_syn_decay_fast : float
202
+ Proportion of fast decay time constant
203
+ a_syn_decay_slow : float
204
+ Proportion of slow decay time constant
205
+ connectivity : float
206
+ Scaling factor for synaptic strength
207
+ """
208
+
209
+ self.tau_syn_decay = tau_syn_decay
210
+ self.T = T
211
+ self.dt = dt
212
+ self.connectivity = connectivity
213
+ self.tau_syn_rise = tau_syn_rise
214
+ self.tau_syn_decay_fast = tau_syn_decay_fast
215
+ self.tau_syn_decay_slow = tau_syn_decay_slow
216
+ self.a_syn_decay_fast = a_syn_decay_fast
217
+ self.a_syn_decay_slow = a_syn_decay_slow
218
+ self.synapse_single_decay = False
219
+ self.synapse_double_decay = False
220
+ self.synapse_single_decay_rise = False
221
+ self.synapse_double_decay_rise = False
222
+
223
+ if tau_syn_decay is not None and (
224
+ tau_syn_decay_slow is None and tau_syn_decay_fast is None and tau_syn_rise is None):
225
+ self.synapse_single_decay = True
226
+
227
+ if (tau_syn_decay_slow is not None and tau_syn_decay_fast is not None) and (
228
+ tau_syn_rise is None and tau_syn_decay is None):
229
+ self.synapse_double_decay = True
230
+
231
+ if (tau_syn_decay_slow is not None and tau_syn_decay_fast is not None and tau_syn_rise is not None) and (
232
+ tau_syn_decay is None):
233
+ self.synapse_double_decay_rise = True
234
+
235
+ if (tau_syn_rise is not None and tau_syn_decay is not None) and (
236
+ tau_syn_decay_slow is None and tau_syn_decay_fast is None):
237
+ self.synapse_single_decay_rise = True
238
+
239
+ if self.synapse_single_decay + self.synapse_double_decay + self.synapse_single_decay_rise + \
240
+ self.synapse_double_decay_rise != 1:
241
+ raise AssertionError("Please check assignment of time constants...")
242
+
243
+ # initialize arrays
244
+ self.t = np.linspace(0, self.T, int(self.T / self.dt + 1))
245
+ self.g_f = np.zeros((N, len(self.t)))
246
+ self.x_f = np.zeros((N, len(self.t)))
247
+ self.g_s = np.zeros((N, len(self.t)))
248
+ self.x_s = np.zeros((N, len(self.t)))
249
+ self.x = np.zeros((N, len(self.t)))
250
+ self.g_tot = np.zeros((N, len(self.t)))
251
+
252
+ def step(self, inp, i):
253
+ """
254
+ Solves the DEQ and computes synapse conductivity
255
+
256
+ Parameters
257
+ ----------
258
+ inp: ndarray of float [n_t]
259
+ Input signal
260
+ i: int
261
+ Current index
262
+ """
263
+
264
+ # double exponential kernel with rise time and slow + fast decay times
265
+ if self.synapse_double_decay_rise:
266
+ self.g_f[:, i] = self.g_f[:, i - 1] + self.x_f[:, i - 1] * self.dt
267
+ self.x_f[:, i] = self.x_f[:, i - 1] + (
268
+ (inp[i] - self.x_f[:, i - 1]) * (self.tau_syn_rise + self.tau_syn_decay_fast) -
269
+ self.g_f[:, i - 1]) / (self.tau_syn_rise * self.tau_syn_decay_fast) * self.dt
270
+ self.g_s[:, i] = self.g_s[:, i - 1] + self.x_s[:, i - 1] * self.dt
271
+ self.x_s[:, i] = self.x_s[:, i - 1] + (
272
+ (inp[i] - self.x_s[:, i - 1]) * (self.tau_syn_rise + self.tau_syn_decay_slow) -
273
+ self.g_s[:, i - 1]) / (self.tau_syn_rise * self.tau_syn_decay_slow) * self.dt
274
+ self.g_tot[:, i] = self.a_syn_decay_fast * self.g_f[:, i] + self.a_syn_decay_slow * self.g_s[:, i]
275
+
276
+ # single exponential kernel with slow + fast decay times
277
+ if self.synapse_double_decay:
278
+ self.g_s[:, i] = self.g_s[:, i - 1] + ((0 - self.g_s[:, i - 1]) / self.tau_syn_decay_slow) * self.dt + inp[
279
+ i]
280
+ self.g_f[:, i] = self.g_f[:, i - 1] + ((0 - self.g_f[:, i - 1]) / self.tau_syn_decay_fast) * self.dt + inp[
281
+ i]
282
+ self.g_tot[:, i] = self.a_syn_decay_fast * self.g_f[:, i] + self.a_syn_decay_slow * self.g_s[:, i]
283
+
284
+ # single exponential kernel with one decay time
285
+ if self.synapse_single_decay:
286
+ self.g_tot[:, i] = self.g_tot[:, i - 1] + ((0 - self.g_tot[:, i - 1]) / self.tau_syn_decay) * self.dt + inp[
287
+ i]
288
+
289
+ # double exponential kernel with rise time and one decay time
290
+ if self.synapse_single_decay_rise:
291
+ self.g_tot[:, i] = self.g_tot[:, i - 1] + self.x[:, i - 1] * self.dt
292
+ self.x[:, i] = self.x[:, i - 1] + ((inp[i] - self.x[:, i - 1]) * (self.tau_syn_rise + self.tau_syn_decay) -
293
+ self.g_tot[:, i - 1]) / (
294
+ self.tau_syn_rise * self.tau_syn_decay) * self.dt
295
+
296
+ return self.g_tot[:, i] * self.connectivity # Apply weight to output
297
+
298
+
299
+ class RenshawCell(object):
300
+ """
301
+ Renshaw Cell (Leaky integrate and fire)
302
+ """
303
+
304
+ def __init__(self, tau_mem, Vm_thr, Vm_rest, Rm, T, dt, delay, synapses, slope):
305
+ """
306
+ Initializes Renshaw Cell instance
307
+
308
+ Parameters
309
+ ----------
310
+ tau_mem : float
311
+ Time constant of cell membrane in ms
312
+ Vm_thr : float
313
+ Firing threshold in mV
314
+ Vm_rest : float
315
+ Resting potential
316
+ Rm : float
317
+ Cell membrane resistance
318
+ T : float
319
+ Total simulation time in ms
320
+ dt : float
321
+ Time-step to solve DEQ
322
+ delay: float
323
+ Delay of feedback in ms
324
+ synapses: list of Synapses
325
+ List of all synapses
326
+ slope : float
327
+ Slope of Threshold
328
+ """
329
+ self.tau_mem = tau_mem
330
+ self.Vm_thr = Vm_thr # TODO proper Threshold
331
+ self.Vm_rest = Vm_rest
332
+ self.Rm = Rm
333
+
334
+ self.T = T
335
+ self.dt = dt
336
+ self.t_rest = 0
337
+
338
+ self.max_firerate = 23.12 # TODO Proper value
339
+ self.slope = slope
340
+ self.delay = delay
341
+
342
+ # Synapses
343
+ self.acetyl_synapse = synapses[0] # excitatory (Alpha Motor Neuron)
344
+
345
+ # initialize arrays
346
+ self.t = np.linspace(0, self.T, int(self.T / self.dt + 1))
347
+ self.g_tot = np.zeros(len(self.t))
348
+ self.Vm = Vm_rest * np.ones(len(self.t), dtype=np.float128)
349
+ self.spike_times = np.zeros(len(self.t))
350
+ self.output = np.zeros(len(self.t), dtype=np.float128)
351
+
352
+ def step(self, inp, t_, i):
353
+ """
354
+ Solves the DEQ and computes membrane potential.
355
+ Saves membrane potential in self.Vm and spike times in self.spike_times
356
+
357
+ Parameters
358
+ ----------
359
+ inp: ndarray of float [n_t]
360
+ Input signal
361
+ t_ : floatf
362
+ Current time step
363
+ i : int
364
+ Current index
365
+ """
366
+
367
+ self.g_tot[i] = np.sum(self.acetyl_synapse.step(inp, i))
368
+
369
+ self.Vm[i] = self.Vm[i - 1] + (-(self.Vm[i - 1] - self.Vm_rest) - self.Vm[i - 1] * self.g_tot[
370
+ i] * self.Rm) / self.tau_mem * self.dt
371
+
372
+ # A. Spiegler potential to Rate
373
+ # np.seterr('raise')
374
+ self.output[i] = 2 * self.max_firerate / (1 + (logsumexp(self.slope * (self.Vm_thr - self.Vm[i]))))
375
+
376
+ return self.output[i]
377
+
378
+ # if(self.Vm[i] >= self.Vm_thr):
379
+ # self.spike_times[i] = t_
380
+ # self.Vm[i] = self.Vm_rest
381
+ # self.t_rest = t_ + self.t_refrac
382
+ # return 1 / self.dt #TODO in Feuerate umwandeln
383
+ #
384
+ # return 0 # return no spike
385
+
386
+
387
+ class AlphaMotorNeuron(object):
388
+ """
389
+ Alpha Motor Neuron (Leaky integrate and fire)
390
+ """
391
+
392
+ def __init__(self, tau_mem, Vm_thr, Vm_rest, Rm, t_refrac, T, dt, synapses, N):
393
+ """
394
+ Initializes AlphaMotorNeuron instance
395
+
396
+ Parameters
397
+ ----------
398
+ tau_mem : float
399
+ Time constant of cell membrane in ms
400
+ Vm_thr : float
401
+ Firing threshold in mV
402
+ Vm_rest : float
403
+ Resting potential
404
+ Rm : float
405
+ Cell membrane resistance
406
+ t_refrac : float
407
+ Refractory time
408
+ T : float
409
+ Total simulation time in ms
410
+ dt : float
411
+ Time-step to solve DEQ
412
+ synapses: list of Synapses
413
+ List of all synapses
414
+ N: int
415
+ Number of alpha motor neurons
416
+ """
417
+ self.tau_mem = tau_mem
418
+ self.Vm_thr = Vm_thr
419
+ self.Vm_rest = Vm_rest
420
+ self.Rm = Rm
421
+ self.t_refrac = t_refrac
422
+ self.T = T
423
+ self.N = N
424
+ self.dt = dt
425
+ self.t_rest = np.zeros(N)
426
+ self.is_in_refrac = np.ones(N)
427
+
428
+ # Synapses
429
+ self.acetyl_synapse = synapses[0] # excitatory (from motor cortex)
430
+ self.glycin_synapse = synapses[1] # inhibitory (from Renshaw cell)
431
+
432
+ # initialize arrays
433
+ self.t = np.linspace(0, self.T, int(self.T / self.dt + 1))
434
+ self.g_tot = np.zeros((N, len(self.t)))
435
+ self.Vm = Vm_rest * np.ones((N, len(self.t)), dtype=np.float128)
436
+ self.spike_times = np.zeros((N, len(self.t)))
437
+
438
+ self.anySpike = 0
439
+
440
+ def step(self, inp, renshaw_feedback, t_, i):
441
+ """
442
+ Solves the DEQ and computes membrane potential.
443
+ Saves membrane potential in self.Vm and spike times in self.spike_times
444
+
445
+ Parameters
446
+ ----------
447
+ inp: ndarray of float [n_t]
448
+ Input signal
449
+ renshaw_feedback: ndarray of float
450
+ Feedback from Renshaw Cell
451
+ t_ : float
452
+ Current time
453
+ i : int
454
+ Current index
455
+ """
456
+ x = 0 # self.glycin_synapse.step(renshaw_feedback, i)
457
+ y = self.acetyl_synapse.step(inp, i)
458
+
459
+ self.g_tot[:, i] = np.where(x + y < 0, 0, x + y)
460
+
461
+ # if t_ > self.t_rest:
462
+ self.Vm[:, i] = self.Vm[:, i - 1] + self.is_in_refrac * \
463
+ ((-(self.Vm[:, i - 1] - self.Vm_rest) - self.Vm[:, i - 1] * self.g_tot[:, i] * self.Rm) /
464
+ self.tau_mem * self.dt)
465
+ firing_neurons = zip(*np.where(self.Vm[:, i] >= self.Vm_thr))
466
+
467
+ for f in firing_neurons:
468
+ if self.is_in_refrac[f] == 1:
469
+ self.spike_times[f, i] = t_
470
+ self.Vm[f, i] = self.Vm_rest
471
+ self.t_rest[f] = t_ + self.t_refrac
472
+ self.is_in_refrac[f] = 0
473
+ self.anySpike = 1
474
+
475
+ for k, t in enumerate(self.t_rest):
476
+ if t_ > self.t_rest[k]:
477
+ self.is_in_refrac[k] = 1
478
+
479
+ # spike event (reset to Vm_rest and pause integrating during refractory period)
480
+ if self.anySpike == 1:
481
+ self.anySpike = 0
482
+ return 1 / self.dt
483
+
484
+ return 0 # return no spike
485
+
486
+
487
+ class MotorUnit(object):
488
+ """
489
+ Motor unit
490
+ """
491
+
492
+ def __init__(self, muap, t):
493
+ """
494
+ Initializes motor unit instance
495
+
496
+ Parameters
497
+ ----------
498
+ muap: ndarray of float [n_t]
499
+ Motor unit action potential
500
+ t: ndarray of float [n_t]
501
+ Time axis corresponding to motor unit action potential
502
+ t_total: ndarray of float [n_t]
503
+ Total time axis of observation (has to fit to t)
504
+ """
505
+
506
+ self.muap = muap
507
+ self.t = t
508
+
509
+ def get_muap(self, spike_times, k):
510
+ """
511
+ Adds MUAPs of the motorunit at given spiketimes
512
+
513
+ Parameters
514
+ ----------
515
+ spike_times: ndarray of float [n_spikes]
516
+ Spike times
517
+
518
+ Returns
519
+ -------
520
+ muap_total: ndarray of float [n_t]
521
+ Total MUAP signal (MUAPs are summed at spiketimes)
522
+ """
523
+ muap_total = np.zeros(len(self.t))
524
+
525
+ for tau in spike_times[k, :]:
526
+ if not tau == 0:
527
+ try:
528
+ idx_tau = np.argmin(np.abs(self.t - tau))
529
+ if len(muap_total) - idx_tau < len(self.muap):
530
+ muap_total[idx_tau:idx_tau + len(self.muap)] += self.muap[:len(muap_total) - idx_tau]
531
+ else:
532
+ muap_total[idx_tau:idx_tau + len(self.muap)] += self.muap
533
+
534
+ except Exception as error:
535
+ print(error)
536
+ return muap_total
537
+
538
+
539
+ class Muscle(object):
540
+ """
541
+ Muscle containing multiple motor unit instances
542
+ """
543
+
544
+ def __init__(self, alpha_motor_neurons, motor_units):
545
+ """
546
+ Initializes muscle instance
547
+
548
+ Parameters
549
+ ----------
550
+ alpha_motor_neurons: list of AlphaMotorNeuron instances
551
+ AlphaMotorNeuron instances of the muscle
552
+ motor_units: list of MotorUnit instances
553
+ MotorUnit instances corresponding to the AlphaMotorNeuron instances
554
+ """
555
+
556
+ self.alpha_motor_neurons = alpha_motor_neurons
557
+ self.motor_units = motor_units
558
+ self.t = self.alpha_motor_neurons.t
559
+ self.N = self.alpha_motor_neurons.N
560
+ self.mep = np.zeros(len(self.t))
561
+
562
+ def get_mep(self):
563
+ for k in range(self.N):
564
+ self.mep += self.motor_units[k].get_muap(self.alpha_motor_neurons.spike_times, k)
565
+
566
+ return self.mep