bmtool 0.6.9.1__py3-none-any.whl → 0.6.9.2__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.
- bmtool/bmplot.py +228 -36
- bmtool/singlecell.py +501 -34
- bmtool/synapses.py +270 -41
- {bmtool-0.6.9.1.dist-info → bmtool-0.6.9.2.dist-info}/METADATA +1 -1
- {bmtool-0.6.9.1.dist-info → bmtool-0.6.9.2.dist-info}/RECORD +9 -9
- {bmtool-0.6.9.1.dist-info → bmtool-0.6.9.2.dist-info}/WHEEL +0 -0
- {bmtool-0.6.9.1.dist-info → bmtool-0.6.9.2.dist-info}/entry_points.txt +0 -0
- {bmtool-0.6.9.1.dist-info → bmtool-0.6.9.2.dist-info}/licenses/LICENSE +0 -0
- {bmtool-0.6.9.1.dist-info → bmtool-0.6.9.2.dist-info}/top_level.txt +0 -0
bmtool/singlecell.py
CHANGED
@@ -11,6 +11,16 @@ import pandas as pd
|
|
11
11
|
|
12
12
|
|
13
13
|
def load_biophys1():
|
14
|
+
"""
|
15
|
+
Load the Biophys1 template from BMTK if it hasn't been loaded yet.
|
16
|
+
|
17
|
+
This function checks if the Biophys1 object exists in NEURON's h namespace.
|
18
|
+
If not, it loads the necessary HOC files for Allen Cell Types Database models.
|
19
|
+
|
20
|
+
Notes:
|
21
|
+
------
|
22
|
+
This is primarily used for working with cell models from the Allen Cell Types Database.
|
23
|
+
"""
|
14
24
|
if not hasattr(h, 'Biophys1'):
|
15
25
|
from bmtk import utils
|
16
26
|
module_dir = os.path.dirname(os.path.abspath(utils.__file__))
|
@@ -20,11 +30,29 @@ def load_biophys1():
|
|
20
30
|
|
21
31
|
|
22
32
|
def load_allen_database_cells(morphology, dynamic_params, model_processing='aibs_perisomatic'):
|
23
|
-
"""
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
33
|
+
"""
|
34
|
+
Create a cell model from the Allen Cell Types Database.
|
35
|
+
|
36
|
+
Parameters:
|
37
|
+
-----------
|
38
|
+
morphology : str
|
39
|
+
Path to the morphology file (SWC or ASC format).
|
40
|
+
dynamic_params : str
|
41
|
+
Path to the JSON file containing biophysical parameters.
|
42
|
+
model_processing : str, optional
|
43
|
+
Model processing type from the AllenCellType database.
|
44
|
+
Default is 'aibs_perisomatic'.
|
45
|
+
|
46
|
+
Returns:
|
47
|
+
--------
|
48
|
+
callable
|
49
|
+
A function that, when called, creates and returns a NEURON cell object
|
50
|
+
with the specified morphology and biophysical properties.
|
51
|
+
|
52
|
+
Notes:
|
53
|
+
------
|
54
|
+
This function creates a closure that loads and returns a cell when called.
|
55
|
+
The cell is created using the Allen Institute's modeling framework.
|
28
56
|
"""
|
29
57
|
from bmtk.simulator.bionet.default_setters import cell_models
|
30
58
|
load_biophys1()
|
@@ -39,6 +67,34 @@ def load_allen_database_cells(morphology, dynamic_params, model_processing='aibs
|
|
39
67
|
|
40
68
|
|
41
69
|
def get_target_site(cell, sec=('soma', 0), loc=0.5, site=''):
|
70
|
+
"""
|
71
|
+
Get a segment and its section from a cell model using flexible section specification.
|
72
|
+
|
73
|
+
Parameters:
|
74
|
+
-----------
|
75
|
+
cell : NEURON cell object
|
76
|
+
The cell object to access sections from.
|
77
|
+
sec : str, int, or tuple, optional
|
78
|
+
Section specification, which can be:
|
79
|
+
- str: Section name (defaults to index 0 if multiple sections)
|
80
|
+
- int: Index into the 'all' section list
|
81
|
+
- tuple: (section_name, index) for accessing indexed sections
|
82
|
+
Default is ('soma', 0).
|
83
|
+
loc : float, optional
|
84
|
+
Location along the section (0-1), default is 0.5 (middle of section).
|
85
|
+
site : str, optional
|
86
|
+
Name of the site for error messages (e.g., 'injection', 'recording').
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
--------
|
90
|
+
tuple
|
91
|
+
(segment, section) at the specified location
|
92
|
+
|
93
|
+
Raises:
|
94
|
+
-------
|
95
|
+
ValueError
|
96
|
+
If the section cannot be found or accessed.
|
97
|
+
"""
|
42
98
|
if isinstance(sec, str):
|
43
99
|
sec = (sec, 0)
|
44
100
|
elif isinstance(sec, int):
|
@@ -64,19 +120,38 @@ class CurrentClamp(object):
|
|
64
120
|
def __init__(self, template_name, post_init_function=None, record_sec='soma', record_loc=0.5, threshold=None,
|
65
121
|
inj_sec='soma', inj_loc=0.5, inj_amp=100., inj_delay=100., inj_dur=1000., tstop=1000.):
|
66
122
|
"""
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
123
|
+
Initialize a current clamp simulation environment.
|
124
|
+
|
125
|
+
Parameters:
|
126
|
+
-----------
|
127
|
+
template_name : str or callable
|
128
|
+
Either the name of the cell template located in HOC or
|
129
|
+
a function that creates and returns a cell object.
|
130
|
+
post_init_function : str, optional
|
131
|
+
Function of the cell to be called after initialization.
|
132
|
+
record_sec : str, int, or tuple, optional
|
133
|
+
Section to record from. Can be:
|
134
|
+
- str: Section name (defaults to index 0 if multiple sections)
|
135
|
+
- int: Index into the 'all' section list
|
136
|
+
- tuple: (section_name, index) for accessing indexed sections
|
137
|
+
Default is 'soma'.
|
138
|
+
record_loc : float, optional
|
139
|
+
Location (0-1) within section to record from. Default is 0.5.
|
140
|
+
threshold : float, optional
|
141
|
+
Spike threshold (mV). If specified, spikes are detected and counted.
|
142
|
+
inj_sec : str, int, or tuple, optional
|
143
|
+
Section for current injection. Same format as record_sec. Default is 'soma'.
|
144
|
+
inj_loc : float, optional
|
145
|
+
Location (0-1) within section for current injection. Default is 0.5.
|
146
|
+
inj_amp : float, optional
|
147
|
+
Current injection amplitude (pA). Default is 100.0.
|
148
|
+
inj_delay : float, optional
|
149
|
+
Start time for current injection (ms). Default is 100.0.
|
150
|
+
inj_dur : float, optional
|
151
|
+
Duration of current injection (ms). Default is 1000.0.
|
152
|
+
tstop : float, optional
|
153
|
+
Total simulation time (ms). Default is 1000.0.
|
154
|
+
Will be extended if necessary to include the full current injection.
|
80
155
|
"""
|
81
156
|
self.create_cell = getattr(h, template_name) if isinstance(template_name, str) else template_name
|
82
157
|
self.record_sec = record_sec
|
@@ -92,7 +167,7 @@ class CurrentClamp(object):
|
|
92
167
|
|
93
168
|
# sometimes people may put a hoc object in for the template name
|
94
169
|
if callable(template_name):
|
95
|
-
self.cell = template_name
|
170
|
+
self.cell = template_name()
|
96
171
|
else:
|
97
172
|
self.cell = self.create_cell()
|
98
173
|
if post_init_function:
|
@@ -101,6 +176,18 @@ class CurrentClamp(object):
|
|
101
176
|
self.setup()
|
102
177
|
|
103
178
|
def setup(self):
|
179
|
+
"""
|
180
|
+
Set up the simulation environment for current clamp experiments.
|
181
|
+
|
182
|
+
This method:
|
183
|
+
1. Creates the current clamp stimulus at the specified injection site
|
184
|
+
2. Sets up voltage recording at the specified recording site
|
185
|
+
3. Creates vectors to store time and voltage data
|
186
|
+
|
187
|
+
Notes:
|
188
|
+
------
|
189
|
+
Sets self.cell_src as the current clamp object that can be accessed later.
|
190
|
+
"""
|
104
191
|
inj_seg, _ = get_target_site(self.cell, self.inj_sec, self.inj_loc, 'injection')
|
105
192
|
self.cell_src = h.IClamp(inj_seg)
|
106
193
|
self.cell_src.delay = self.inj_delay
|
@@ -124,6 +211,21 @@ class CurrentClamp(object):
|
|
124
211
|
print(f'Recording: {rec_seg}._ref_v')
|
125
212
|
|
126
213
|
def execute(self) -> Tuple[list, list]:
|
214
|
+
"""
|
215
|
+
Run the current clamp simulation and return recorded data.
|
216
|
+
|
217
|
+
This method:
|
218
|
+
1. Sets up the simulation duration
|
219
|
+
2. Initializes and runs the NEURON simulation
|
220
|
+
3. Converts recorded vectors to Python lists
|
221
|
+
|
222
|
+
Returns:
|
223
|
+
--------
|
224
|
+
tuple
|
225
|
+
(time_vector, voltage_vector) where:
|
226
|
+
- time_vector: List of time points (ms)
|
227
|
+
- voltage_vector: List of membrane potentials (mV) at those time points
|
228
|
+
"""
|
127
229
|
print("Current clamp simulation running...")
|
128
230
|
h.tstop = self.tstop
|
129
231
|
h.stdinit()
|
@@ -141,11 +243,39 @@ class Passive(CurrentClamp):
|
|
141
243
|
def __init__(self, template_name, inj_amp=-100., inj_delay=200., inj_dur=1000.,
|
142
244
|
tstop=1200., method=None, **kwargs):
|
143
245
|
"""
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
246
|
+
Initialize a passive membrane property simulation environment.
|
247
|
+
|
248
|
+
Parameters:
|
249
|
+
-----------
|
250
|
+
template_name : str or callable
|
251
|
+
Either the name of the cell template located in HOC or
|
252
|
+
a function that creates and returns a cell object.
|
253
|
+
inj_amp : float, optional
|
254
|
+
Current injection amplitude (pA). Default is -100.0 (negative to measure passive properties).
|
255
|
+
inj_delay : float, optional
|
256
|
+
Start time for current injection (ms). Default is 200.0.
|
257
|
+
inj_dur : float, optional
|
258
|
+
Duration of current injection (ms). Default is 1000.0.
|
259
|
+
tstop : float, optional
|
260
|
+
Total simulation time (ms). Default is 1200.0.
|
261
|
+
method : str, optional
|
262
|
+
Method to estimate membrane time constant:
|
263
|
+
- 'simple': Find the time to reach 0.632 of voltage change
|
264
|
+
- 'exp': Fit a single exponential curve
|
265
|
+
- 'exp2': Fit a double exponential curve
|
266
|
+
Default is None, which uses 'simple' when calculations are performed.
|
267
|
+
**kwargs :
|
268
|
+
Additional keyword arguments to pass to the parent CurrentClamp constructor.
|
269
|
+
|
270
|
+
Notes:
|
271
|
+
------
|
272
|
+
This class is designed for measuring passive membrane properties including
|
273
|
+
input resistance and membrane time constant.
|
274
|
+
|
275
|
+
Raises:
|
276
|
+
-------
|
277
|
+
AssertionError
|
278
|
+
If inj_amp is zero (must be non-zero to measure passive properties).
|
149
279
|
"""
|
150
280
|
assert(inj_amp != 0)
|
151
281
|
super().__init__(template_name=template_name, tstop=tstop,
|
@@ -155,6 +285,23 @@ class Passive(CurrentClamp):
|
|
155
285
|
self.tau_methods = {'simple': self.tau_simple, 'exp2': self.tau_double_exponential, 'exp': self.tau_single_exponential}
|
156
286
|
|
157
287
|
def tau_simple(self):
|
288
|
+
"""
|
289
|
+
Calculate membrane time constant using the simple 0.632 criterion method.
|
290
|
+
|
291
|
+
This method calculates the membrane time constant by finding the time it takes
|
292
|
+
for the membrane potential to reach 63.2% (1-1/e) of its final value after
|
293
|
+
a step current injection.
|
294
|
+
|
295
|
+
Returns:
|
296
|
+
--------
|
297
|
+
callable
|
298
|
+
A function that prints the calculation details when called.
|
299
|
+
|
300
|
+
Notes:
|
301
|
+
------
|
302
|
+
Sets the following attributes:
|
303
|
+
- tau: The calculated membrane time constant in ms
|
304
|
+
"""
|
158
305
|
v_t_const = self.cell_v_final - self.v_diff / np.e
|
159
306
|
index_v_tau = next(x for x, val in enumerate(self.v_vec_inj) if val <= v_t_const)
|
160
307
|
self.tau = self.t_vec[self.index_v_rest + index_v_tau] - self.v_rest_time # ms
|
@@ -171,9 +318,53 @@ class Passive(CurrentClamp):
|
|
171
318
|
|
172
319
|
@staticmethod
|
173
320
|
def single_exponential(t, a0, a, tau):
|
321
|
+
"""
|
322
|
+
Single exponential function for fitting membrane potential response.
|
323
|
+
|
324
|
+
Parameters:
|
325
|
+
-----------
|
326
|
+
t : array-like
|
327
|
+
Time values
|
328
|
+
a0 : float
|
329
|
+
Offset (steady-state) value
|
330
|
+
a : float
|
331
|
+
Amplitude of the exponential component
|
332
|
+
tau : float
|
333
|
+
Time constant of the exponential decay
|
334
|
+
|
335
|
+
Returns:
|
336
|
+
--------
|
337
|
+
array-like
|
338
|
+
Function values at the given time points
|
339
|
+
"""
|
174
340
|
return a0 + a * np.exp(-t / tau)
|
175
341
|
|
176
342
|
def tau_single_exponential(self):
|
343
|
+
"""
|
344
|
+
Calculate membrane time constant by fitting a single exponential curve.
|
345
|
+
|
346
|
+
This method:
|
347
|
+
1. Identifies the peak response (for sag characterization)
|
348
|
+
2. Falls back to simple method for initial estimate
|
349
|
+
3. Fits a single exponential function to the membrane potential response
|
350
|
+
4. Sets tau to the exponential time constant
|
351
|
+
|
352
|
+
Returns:
|
353
|
+
--------
|
354
|
+
callable
|
355
|
+
A function that prints the calculation details when called.
|
356
|
+
|
357
|
+
Notes:
|
358
|
+
------
|
359
|
+
Sets the following attributes:
|
360
|
+
- tau: The calculated membrane time constant in ms
|
361
|
+
- t_peak, v_peak: Time and voltage of peak response
|
362
|
+
- v_sag: Sag potential (difference between peak and steady-state)
|
363
|
+
- v_max_diff: Maximum potential difference from rest
|
364
|
+
- sag_norm: Normalized sag ratio
|
365
|
+
- popt: Optimized parameters from curve fitting
|
366
|
+
- pcov: Covariance matrix of the optimization
|
367
|
+
"""
|
177
368
|
index_v_peak = (np.sign(self.inj_amp) * self.v_vec_inj).argmax()
|
178
369
|
self.t_peak = self.t_vec_inj[index_v_peak]
|
179
370
|
self.v_peak = self.v_vec_inj[index_v_peak]
|
@@ -206,9 +397,57 @@ class Passive(CurrentClamp):
|
|
206
397
|
|
207
398
|
@staticmethod
|
208
399
|
def double_exponential(t, a0, a1, a2, tau1, tau2):
|
400
|
+
"""
|
401
|
+
Double exponential function for fitting membrane potential response.
|
402
|
+
|
403
|
+
This function is particularly useful for modeling cells with sag responses,
|
404
|
+
where the membrane potential shows two distinct time constants.
|
405
|
+
|
406
|
+
Parameters:
|
407
|
+
-----------
|
408
|
+
t : array-like
|
409
|
+
Time values
|
410
|
+
a0 : float
|
411
|
+
Offset (steady-state) value
|
412
|
+
a1 : float
|
413
|
+
Amplitude of the first exponential component
|
414
|
+
a2 : float
|
415
|
+
Amplitude of the second exponential component
|
416
|
+
tau1 : float
|
417
|
+
Time constant of the first exponential component
|
418
|
+
tau2 : float
|
419
|
+
Time constant of the second exponential component
|
420
|
+
|
421
|
+
Returns:
|
422
|
+
--------
|
423
|
+
array-like
|
424
|
+
Function values at the given time points
|
425
|
+
"""
|
209
426
|
return a0 + a1 * np.exp(-t / tau1) + a2 * np.exp(-t / tau2)
|
210
427
|
|
211
428
|
def tau_double_exponential(self):
|
429
|
+
"""
|
430
|
+
Calculate membrane time constant by fitting a double exponential curve.
|
431
|
+
|
432
|
+
This method is useful for cells with sag responses that cannot be
|
433
|
+
fitted well with a single exponential.
|
434
|
+
|
435
|
+
Returns:
|
436
|
+
--------
|
437
|
+
callable
|
438
|
+
A function that prints the calculation details when called.
|
439
|
+
|
440
|
+
Notes:
|
441
|
+
------
|
442
|
+
Sets the following attributes:
|
443
|
+
- tau: The calculated membrane time constant (the slower of the two time constants)
|
444
|
+
- t_peak, v_peak: Time and voltage of peak response
|
445
|
+
- v_sag: Sag potential (difference between peak and steady-state)
|
446
|
+
- v_max_diff: Maximum potential difference from rest
|
447
|
+
- sag_norm: Normalized sag ratio
|
448
|
+
- popt: Optimized parameters from curve fitting
|
449
|
+
- pcov: Covariance matrix of the optimization
|
450
|
+
"""
|
212
451
|
index_v_peak = (np.sign(self.inj_amp) * self.v_vec_inj).argmax()
|
213
452
|
self.t_peak = self.t_vec_inj[index_v_peak]
|
214
453
|
self.v_peak = self.v_vec_inj[index_v_peak]
|
@@ -240,16 +479,58 @@ class Passive(CurrentClamp):
|
|
240
479
|
return print_calc
|
241
480
|
|
242
481
|
def double_exponential_fit(self):
|
482
|
+
"""
|
483
|
+
Get the double exponential fit values for plotting.
|
484
|
+
|
485
|
+
Returns:
|
486
|
+
--------
|
487
|
+
tuple
|
488
|
+
(time_vector, fitted_values) where:
|
489
|
+
- time_vector: Time points starting from rest time
|
490
|
+
- fitted_values: Membrane potential values predicted by the double exponential function
|
491
|
+
"""
|
243
492
|
t_vec = self.v_rest_time + self.t_vec_inj
|
244
493
|
v_fit = self.double_exponential(self.t_vec_inj, *self.popt)
|
245
494
|
return t_vec, v_fit
|
246
495
|
|
247
496
|
def single_exponential_fit(self):
|
497
|
+
"""
|
498
|
+
Get the single exponential fit values for plotting.
|
499
|
+
|
500
|
+
Returns:
|
501
|
+
--------
|
502
|
+
tuple
|
503
|
+
(time_vector, fitted_values) where:
|
504
|
+
- time_vector: Time points starting from rest time
|
505
|
+
- fitted_values: Membrane potential values predicted by the single exponential function
|
506
|
+
"""
|
248
507
|
t_vec = self.v_rest_time + self.t_vec_inj
|
249
508
|
v_fit = self.single_exponential(self.t_vec_inj, *self.popt)
|
250
509
|
return t_vec, v_fit
|
251
510
|
|
252
511
|
def execute(self):
|
512
|
+
"""
|
513
|
+
Run the simulation and calculate passive membrane properties.
|
514
|
+
|
515
|
+
This method:
|
516
|
+
1. Runs the NEURON simulation
|
517
|
+
2. Extracts membrane potential at rest and steady-state
|
518
|
+
3. Calculates input resistance from the step response
|
519
|
+
4. Calculates membrane time constant using the specified method
|
520
|
+
5. Prints detailed calculations for educational purposes
|
521
|
+
|
522
|
+
Returns:
|
523
|
+
--------
|
524
|
+
tuple
|
525
|
+
(time_vector, voltage_vector) from the simulation
|
526
|
+
|
527
|
+
Notes:
|
528
|
+
------
|
529
|
+
Sets several attributes including:
|
530
|
+
- v_rest: Resting membrane potential
|
531
|
+
- r_in: Input resistance in MOhms
|
532
|
+
- tau: Membrane time constant in ms
|
533
|
+
"""
|
253
534
|
print("Running simulation for passive properties...")
|
254
535
|
h.tstop = self.tstop
|
255
536
|
h.stdinit()
|
@@ -292,13 +573,41 @@ class FI(object):
|
|
292
573
|
i_start=0., i_stop=1050., i_increment=100., tstart=50., tdur=1000., threshold=0.,
|
293
574
|
record_sec='soma', record_loc=0.5, inj_sec='soma', inj_loc=0.5):
|
294
575
|
"""
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
576
|
+
Initialize a frequency-current (F-I) curve simulation environment.
|
577
|
+
|
578
|
+
Parameters:
|
579
|
+
-----------
|
580
|
+
template_name : str or callable
|
581
|
+
Either the name of the cell template located in HOC or
|
582
|
+
a function that creates and returns a cell object.
|
583
|
+
post_init_function : str, optional
|
584
|
+
Function of the cell to be called after initialization.
|
585
|
+
i_start : float, optional
|
586
|
+
Initial current injection amplitude (pA). Default is 0.0.
|
587
|
+
i_stop : float, optional
|
588
|
+
Maximum current injection amplitude (pA). Default is 1050.0.
|
589
|
+
i_increment : float, optional
|
590
|
+
Amplitude increment between trials (pA). Default is 100.0.
|
591
|
+
tstart : float, optional
|
592
|
+
Current injection start time (ms). Default is 50.0.
|
593
|
+
tdur : float, optional
|
594
|
+
Current injection duration (ms). Default is 1000.0.
|
595
|
+
threshold : float, optional
|
596
|
+
Spike threshold (mV). Default is 0.0.
|
597
|
+
record_sec : str, int, or tuple, optional
|
598
|
+
Section to record from. Same format as in CurrentClamp. Default is 'soma'.
|
599
|
+
record_loc : float, optional
|
600
|
+
Location (0-1) within section to record from. Default is 0.5.
|
601
|
+
inj_sec : str, int, or tuple, optional
|
602
|
+
Section for current injection. Same format as record_sec. Default is 'soma'.
|
603
|
+
inj_loc : float, optional
|
604
|
+
Location (0-1) within section for current injection. Default is 0.5.
|
605
|
+
|
606
|
+
Notes:
|
607
|
+
------
|
608
|
+
This class creates multiple instances of the cell model, one for each
|
609
|
+
current amplitude to be tested, allowing all simulations to be run
|
610
|
+
in a single call to NEURON's run() function.
|
302
611
|
"""
|
303
612
|
self.create_cell = getattr(h, template_name) if isinstance(template_name, str) else template_name
|
304
613
|
self.post_init_function = post_init_function
|
@@ -333,6 +642,19 @@ class FI(object):
|
|
333
642
|
self.setup()
|
334
643
|
|
335
644
|
def setup(self):
|
645
|
+
"""
|
646
|
+
Set up the simulation environment for frequency-current (F-I) analysis.
|
647
|
+
|
648
|
+
For each current amplitude to be tested, this method:
|
649
|
+
1. Creates a current source at the injection site
|
650
|
+
2. Sets up spike detection at the recording site
|
651
|
+
3. Creates vectors to record spike times
|
652
|
+
|
653
|
+
Notes:
|
654
|
+
------
|
655
|
+
This preparation allows multiple simulations to be run with different
|
656
|
+
current amplitudes in a single call to h.run().
|
657
|
+
"""
|
336
658
|
for cell, amp in zip(self.cells, self.amps):
|
337
659
|
inj_seg, _ = get_target_site(cell, self.inj_sec, self.inj_loc, 'injection')
|
338
660
|
src = h.IClamp(inj_seg)
|
@@ -353,6 +675,21 @@ class FI(object):
|
|
353
675
|
print(f'Recording: {rec_seg}._ref_v')
|
354
676
|
|
355
677
|
def execute(self):
|
678
|
+
"""
|
679
|
+
Run the simulation and count spikes for each current amplitude.
|
680
|
+
|
681
|
+
This method:
|
682
|
+
1. Initializes and runs a single NEURON simulation that evaluates all current amplitudes
|
683
|
+
2. Counts spikes for each current amplitude
|
684
|
+
3. Prints a summary of results in tabular format
|
685
|
+
|
686
|
+
Returns:
|
687
|
+
--------
|
688
|
+
tuple
|
689
|
+
(current_amplitudes, spike_counts) where:
|
690
|
+
- current_amplitudes: List of current injection amplitudes (nA)
|
691
|
+
- spike_counts: List of spike counts corresponding to each amplitude
|
692
|
+
"""
|
356
693
|
print("Running simulations for FI curve...")
|
357
694
|
h.tstop = self.tstop
|
358
695
|
h.stdinit()
|
@@ -376,9 +713,42 @@ class ZAP(CurrentClamp):
|
|
376
713
|
def __init__(self, template_name, inj_amp=100., inj_delay=200., inj_dur=15000.,
|
377
714
|
tstop=15500., fstart=0., fend=15., chirp_type=None, **kwargs):
|
378
715
|
"""
|
379
|
-
|
380
|
-
|
381
|
-
|
716
|
+
Initialize a ZAP (impedance amplitude profile) simulation environment.
|
717
|
+
|
718
|
+
Parameters:
|
719
|
+
-----------
|
720
|
+
template_name : str or callable
|
721
|
+
Either the name of the cell template located in HOC or
|
722
|
+
a function that creates and returns a cell object.
|
723
|
+
inj_amp : float, optional
|
724
|
+
Current injection amplitude (pA). Default is 100.0.
|
725
|
+
inj_delay : float, optional
|
726
|
+
Start time for current injection (ms). Default is 200.0.
|
727
|
+
inj_dur : float, optional
|
728
|
+
Duration of current injection (ms). Default is 15000.0.
|
729
|
+
tstop : float, optional
|
730
|
+
Total simulation time (ms). Default is 15500.0.
|
731
|
+
fstart : float, optional
|
732
|
+
Starting frequency of the chirp current (Hz). Default is 0.0.
|
733
|
+
fend : float, optional
|
734
|
+
Ending frequency of the chirp current (Hz). Default is 15.0.
|
735
|
+
chirp_type : str, optional
|
736
|
+
Type of chirp current determining how frequency increases over time:
|
737
|
+
- 'linear': Linear increase in frequency (default if None)
|
738
|
+
- 'exponential': Exponential increase in frequency
|
739
|
+
**kwargs :
|
740
|
+
Additional keyword arguments to pass to the parent CurrentClamp constructor.
|
741
|
+
|
742
|
+
Notes:
|
743
|
+
------
|
744
|
+
This class is designed for measuring the frequency-dependent impedance profile
|
745
|
+
of a neuron using a chirp current that sweeps through frequencies.
|
746
|
+
|
747
|
+
Raises:
|
748
|
+
-------
|
749
|
+
AssertionError
|
750
|
+
- If inj_amp is zero
|
751
|
+
- If chirp_type is 'exponential' and either fstart or fend is <= 0
|
382
752
|
"""
|
383
753
|
assert(inj_amp != 0)
|
384
754
|
super().__init__(template_name=template_name, tstop=tstop,
|
@@ -392,13 +762,67 @@ class ZAP(CurrentClamp):
|
|
392
762
|
assert(fstart > 0 and fend > 0)
|
393
763
|
|
394
764
|
def linear_chirp(self, t, f0, f1):
|
765
|
+
"""
|
766
|
+
Generate a chirp current with linearly increasing frequency.
|
767
|
+
|
768
|
+
Parameters:
|
769
|
+
-----------
|
770
|
+
t : ndarray
|
771
|
+
Time vector (ms)
|
772
|
+
f0 : float
|
773
|
+
Start frequency (kHz)
|
774
|
+
f1 : float
|
775
|
+
End frequency (kHz)
|
776
|
+
|
777
|
+
Returns:
|
778
|
+
--------
|
779
|
+
ndarray
|
780
|
+
Current values with amplitude self.inj_amp and frequency
|
781
|
+
increasing linearly from f0 to f1 Hz over time t
|
782
|
+
"""
|
395
783
|
return self.inj_amp * np.sin(np.pi * (2 * f0 + (f1 - f0) / t[-1] * t) * t)
|
396
784
|
|
397
785
|
def exponential_chirp(self, t, f0, f1):
|
786
|
+
"""
|
787
|
+
Generate a chirp current with exponentially increasing frequency.
|
788
|
+
|
789
|
+
Parameters:
|
790
|
+
-----------
|
791
|
+
t : ndarray
|
792
|
+
Time vector (ms)
|
793
|
+
f0 : float
|
794
|
+
Start frequency (kHz), must be > 0
|
795
|
+
f1 : float
|
796
|
+
End frequency (kHz), must be > 0
|
797
|
+
|
798
|
+
Returns:
|
799
|
+
--------
|
800
|
+
ndarray
|
801
|
+
Current values with amplitude self.inj_amp and frequency
|
802
|
+
increasing exponentially from f0 to f1 Hz over time t
|
803
|
+
|
804
|
+
Notes:
|
805
|
+
------
|
806
|
+
For exponential chirp, both f0 and f1 must be positive.
|
807
|
+
"""
|
398
808
|
L = np.log(f1 / f0) / t[-1]
|
399
809
|
return self.inj_amp * np.sin(np.pi * 2 * f0 / L * (np.exp(L * t) - 1))
|
400
810
|
|
401
811
|
def zap_current(self):
|
812
|
+
"""
|
813
|
+
Create a frequency-modulated (chirp) current for probing impedance.
|
814
|
+
|
815
|
+
This method:
|
816
|
+
1. Sets up time vectors for the simulation and current injection
|
817
|
+
2. Creates a chirp current based on the specified parameters (linear or exponential)
|
818
|
+
3. Prepares the current vector for NEURON playback
|
819
|
+
|
820
|
+
Notes:
|
821
|
+
------
|
822
|
+
The chirp current increases in frequency from fstart to fend Hz over the duration
|
823
|
+
of the injection. This allows frequency-dependent impedance to be measured in
|
824
|
+
a single simulation.
|
825
|
+
"""
|
402
826
|
self.dt = dt = h.dt
|
403
827
|
self.index_v_rest = int(self.inj_delay / dt)
|
404
828
|
self.index_v_final = int(self.inj_stop / dt)
|
@@ -417,6 +841,28 @@ class ZAP(CurrentClamp):
|
|
417
841
|
self.zap_vec.play(self.cell_src._ref_amp, dt)
|
418
842
|
|
419
843
|
def get_impedance(self, smooth=1):
|
844
|
+
"""
|
845
|
+
Calculate and extract the frequency-dependent impedance profile.
|
846
|
+
|
847
|
+
This method:
|
848
|
+
1. Filters the impedance to the frequency range of interest
|
849
|
+
2. Optionally applies smoothing to reduce noise
|
850
|
+
3. Identifies the resonant frequency (peak impedance)
|
851
|
+
|
852
|
+
Parameters:
|
853
|
+
-----------
|
854
|
+
smooth : int, optional
|
855
|
+
Window size for smoothing the impedance. Default is 1 (no smoothing).
|
856
|
+
|
857
|
+
Returns:
|
858
|
+
--------
|
859
|
+
tuple
|
860
|
+
(frequencies, impedance_values) in the range of interest
|
861
|
+
|
862
|
+
Notes:
|
863
|
+
------
|
864
|
+
Sets self.peak_freq to the resonant frequency (frequency of maximum impedance).
|
865
|
+
"""
|
420
866
|
f_idx = (self.freq > min(self.fstart, self.fend)) & (self.freq < max(self.fstart, self.fend))
|
421
867
|
impedance = self.impedance
|
422
868
|
if smooth > 1:
|
@@ -427,6 +873,27 @@ class ZAP(CurrentClamp):
|
|
427
873
|
return freq, impedance
|
428
874
|
|
429
875
|
def execute(self) -> Tuple[list, list]:
|
876
|
+
"""
|
877
|
+
Run the ZAP simulation and calculate the impedance profile.
|
878
|
+
|
879
|
+
This method:
|
880
|
+
1. Sets up the chirp current
|
881
|
+
2. Runs the NEURON simulation
|
882
|
+
3. Calculates the impedance using FFT
|
883
|
+
4. Prints a summary of the frequency range and analysis method
|
884
|
+
|
885
|
+
Returns:
|
886
|
+
--------
|
887
|
+
tuple
|
888
|
+
(time_vector, voltage_vector) from the simulation
|
889
|
+
|
890
|
+
Notes:
|
891
|
+
------
|
892
|
+
Sets several attributes including:
|
893
|
+
- Z: Complex impedance values (from FFT)
|
894
|
+
- freq: Frequency values for the impedance profile
|
895
|
+
- impedance: Absolute impedance values
|
896
|
+
"""
|
430
897
|
print("ZAP current simulation running...")
|
431
898
|
self.zap_current()
|
432
899
|
h.tstop = self.tstop
|