bmtool 0.7.0.6.2__py3-none-any.whl → 0.7.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.
bmtool/singlecell.py CHANGED
@@ -1,38 +1,40 @@
1
1
  import glob
2
- import os
3
2
  import json
3
+ import os
4
4
  from typing import Tuple
5
- import numpy as np
5
+
6
6
  import matplotlib.pyplot as plt
7
- from scipy.optimize import curve_fit
8
7
  import neuron
9
- from neuron import h
8
+ import numpy as np
10
9
  import pandas as pd
10
+ from neuron import h
11
+ from scipy.optimize import curve_fit
11
12
 
12
13
 
13
14
  def load_biophys1():
14
15
  """
15
16
  Load the Biophys1 template from BMTK if it hasn't been loaded yet.
16
-
17
+
17
18
  This function checks if the Biophys1 object exists in NEURON's h namespace.
18
19
  If not, it loads the necessary HOC files for Allen Cell Types Database models.
19
-
20
+
20
21
  Notes:
21
22
  ------
22
23
  This is primarily used for working with cell models from the Allen Cell Types Database.
23
24
  """
24
- if not hasattr(h, 'Biophys1'):
25
+ if not hasattr(h, "Biophys1"):
25
26
  from bmtk import utils
27
+
26
28
  module_dir = os.path.dirname(os.path.abspath(utils.__file__))
27
- hoc_file = os.path.join(module_dir, 'scripts', 'bionet', 'templates', 'Biophys1.hoc')
29
+ hoc_file = os.path.join(module_dir, "scripts", "bionet", "templates", "Biophys1.hoc")
28
30
  h.load_file("import3d.hoc")
29
31
  h.load_file(hoc_file)
30
32
 
31
33
 
32
- def load_allen_database_cells(morphology, dynamic_params, model_processing='aibs_perisomatic'):
34
+ def load_allen_database_cells(morphology, dynamic_params, model_processing="aibs_perisomatic"):
33
35
  """
34
36
  Create a cell model from the Allen Cell Types Database.
35
-
37
+
36
38
  Parameters:
37
39
  -----------
38
40
  morphology : str
@@ -42,34 +44,37 @@ def load_allen_database_cells(morphology, dynamic_params, model_processing='aibs
42
44
  model_processing : str, optional
43
45
  Model processing type from the AllenCellType database.
44
46
  Default is 'aibs_perisomatic'.
45
-
47
+
46
48
  Returns:
47
49
  --------
48
50
  callable
49
51
  A function that, when called, creates and returns a NEURON cell object
50
52
  with the specified morphology and biophysical properties.
51
-
53
+
52
54
  Notes:
53
55
  ------
54
56
  This function creates a closure that loads and returns a cell when called.
55
57
  The cell is created using the Allen Institute's modeling framework.
56
58
  """
57
59
  from bmtk.simulator.bionet.default_setters import cell_models
60
+
58
61
  load_biophys1()
59
62
  model_processing = getattr(cell_models, model_processing)
60
63
  with open(dynamic_params) as f:
61
64
  dynamics_params = json.load(f)
65
+
62
66
  def create_cell():
63
67
  hobj = h.Biophys1(morphology)
64
68
  hobj = model_processing(hobj, cell=None, dynamics_params=dynamics_params)
65
69
  return hobj
70
+
66
71
  return create_cell
67
72
 
68
73
 
69
- def get_target_site(cell, sec=('soma', 0), loc=0.5, site=''):
74
+ def get_target_site(cell, sec=("soma", 0), loc=0.5, site=""):
70
75
  """
71
76
  Get a segment and its section from a cell model using flexible section specification.
72
-
77
+
73
78
  Parameters:
74
79
  -----------
75
80
  cell : NEURON cell object
@@ -84,12 +89,12 @@ def get_target_site(cell, sec=('soma', 0), loc=0.5, site=''):
84
89
  Location along the section (0-1), default is 0.5 (middle of section).
85
90
  site : str, optional
86
91
  Name of the site for error messages (e.g., 'injection', 'recording').
87
-
92
+
88
93
  Returns:
89
94
  --------
90
95
  tuple
91
96
  (segment, section) at the specified location
92
-
97
+
93
98
  Raises:
94
99
  -------
95
100
  ValueError
@@ -98,9 +103,9 @@ def get_target_site(cell, sec=('soma', 0), loc=0.5, site=''):
98
103
  if isinstance(sec, str):
99
104
  sec = (sec, 0)
100
105
  elif isinstance(sec, int):
101
- if not hasattr(cell, 'all'):
106
+ if not hasattr(cell, "all"):
102
107
  raise ValueError("Section list named 'all' does not exist in the template.")
103
- sec = ('all', sec)
108
+ sec = ("all", sec)
104
109
  loc = float(loc)
105
110
  try:
106
111
  section = next(s for i, s in enumerate(getattr(cell, sec[0])) if i == sec[1])
@@ -117,11 +122,23 @@ def get_target_site(cell, sec=('soma', 0), loc=0.5, site=''):
117
122
 
118
123
 
119
124
  class CurrentClamp(object):
120
- def __init__(self, template_name, post_init_function=None, record_sec='soma', record_loc=0.5, threshold=None,
121
- inj_sec='soma', inj_loc=0.5, inj_amp=100., inj_delay=100., inj_dur=1000., tstop=1000.):
125
+ def __init__(
126
+ self,
127
+ template_name,
128
+ post_init_function=None,
129
+ record_sec="soma",
130
+ record_loc=0.5,
131
+ threshold=None,
132
+ inj_sec="soma",
133
+ inj_loc=0.5,
134
+ inj_amp=100.0,
135
+ inj_delay=100.0,
136
+ inj_dur=1000.0,
137
+ tstop=1000.0,
138
+ ):
122
139
  """
123
140
  Initialize a current clamp simulation environment.
124
-
141
+
125
142
  Parameters:
126
143
  -----------
127
144
  template_name : str or callable
@@ -153,7 +170,9 @@ class CurrentClamp(object):
153
170
  Total simulation time (ms). Default is 1000.0.
154
171
  Will be extended if necessary to include the full current injection.
155
172
  """
156
- self.create_cell = getattr(h, template_name) if isinstance(template_name, str) else template_name
173
+ self.create_cell = (
174
+ getattr(h, template_name) if isinstance(template_name, str) else template_name
175
+ )
157
176
  self.record_sec = record_sec
158
177
  self.record_loc = record_loc
159
178
  self.inj_sec = inj_sec
@@ -161,14 +180,14 @@ class CurrentClamp(object):
161
180
  self.threshold = threshold
162
181
 
163
182
  self.tstop = max(tstop, inj_delay + inj_dur)
164
- self.inj_delay = inj_delay # use x ms after start of inj to calculate r_in, etc
183
+ self.inj_delay = inj_delay # use x ms after start of inj to calculate r_in, etc
165
184
  self.inj_dur = inj_dur
166
- self.inj_amp = inj_amp * 1e-3 # pA to nA
185
+ self.inj_amp = inj_amp * 1e-3 # pA to nA
167
186
 
168
- # sometimes people may put a hoc object in for the template name
187
+ # sometimes people may put a hoc object in for the template name
169
188
  if callable(template_name):
170
189
  self.cell = template_name()
171
- else:
190
+ else:
172
191
  self.cell = self.create_cell()
173
192
  if post_init_function:
174
193
  eval(f"self.cell.{post_init_function}")
@@ -178,23 +197,23 @@ class CurrentClamp(object):
178
197
  def setup(self):
179
198
  """
180
199
  Set up the simulation environment for current clamp experiments.
181
-
200
+
182
201
  This method:
183
202
  1. Creates the current clamp stimulus at the specified injection site
184
203
  2. Sets up voltage recording at the specified recording site
185
204
  3. Creates vectors to store time and voltage data
186
-
205
+
187
206
  Notes:
188
207
  ------
189
208
  Sets self.cell_src as the current clamp object that can be accessed later.
190
209
  """
191
- inj_seg, _ = get_target_site(self.cell, self.inj_sec, self.inj_loc, 'injection')
210
+ inj_seg, _ = get_target_site(self.cell, self.inj_sec, self.inj_loc, "injection")
192
211
  self.cell_src = h.IClamp(inj_seg)
193
212
  self.cell_src.delay = self.inj_delay
194
213
  self.cell_src.dur = self.inj_dur
195
214
  self.cell_src.amp = self.inj_amp
196
215
 
197
- rec_seg, rec_sec = get_target_site(self.cell, self.record_sec, self.record_loc, 'recording')
216
+ rec_seg, rec_sec = get_target_site(self.cell, self.record_sec, self.record_loc, "recording")
198
217
  self.v_vec = h.Vector()
199
218
  self.v_vec.record(rec_seg._ref_v)
200
219
 
@@ -207,18 +226,18 @@ class CurrentClamp(object):
207
226
  self.tspk_vec = h.Vector()
208
227
  self.nc.record(self.tspk_vec)
209
228
 
210
- print(f'Injection location: {inj_seg}')
211
- print(f'Recording: {rec_seg}._ref_v')
229
+ print(f"Injection location: {inj_seg}")
230
+ print(f"Recording: {rec_seg}._ref_v")
212
231
 
213
232
  def execute(self) -> Tuple[list, list]:
214
233
  """
215
234
  Run the current clamp simulation and return recorded data.
216
-
235
+
217
236
  This method:
218
237
  1. Sets up the simulation duration
219
238
  2. Initializes and runs the NEURON simulation
220
239
  3. Converts recorded vectors to Python lists
221
-
240
+
222
241
  Returns:
223
242
  --------
224
243
  tuple
@@ -234,17 +253,25 @@ class CurrentClamp(object):
234
253
  if self.threshold is not None:
235
254
  self.nspks = len(self.tspk_vec)
236
255
  print()
237
- print(f'Number of spikes: {self.nspks:d}')
256
+ print(f"Number of spikes: {self.nspks:d}")
238
257
  print()
239
258
  return self.t_vec.to_python(), self.v_vec.to_python()
240
259
 
241
260
 
242
261
  class Passive(CurrentClamp):
243
- def __init__(self, template_name, inj_amp=-100., inj_delay=200., inj_dur=1000.,
244
- tstop=1200., method=None, **kwargs):
262
+ def __init__(
263
+ self,
264
+ template_name,
265
+ inj_amp=-100.0,
266
+ inj_delay=200.0,
267
+ inj_dur=1000.0,
268
+ tstop=1200.0,
269
+ method=None,
270
+ **kwargs,
271
+ ):
245
272
  """
246
273
  Initialize a passive membrane property simulation environment.
247
-
274
+
248
275
  Parameters:
249
276
  -----------
250
277
  template_name : str or callable
@@ -264,39 +291,49 @@ class Passive(CurrentClamp):
264
291
  - 'exp': Fit a single exponential curve
265
292
  - 'exp2': Fit a double exponential curve
266
293
  Default is None, which uses 'simple' when calculations are performed.
267
- **kwargs :
294
+ **kwargs :
268
295
  Additional keyword arguments to pass to the parent CurrentClamp constructor.
269
-
296
+
270
297
  Notes:
271
298
  ------
272
299
  This class is designed for measuring passive membrane properties including
273
300
  input resistance and membrane time constant.
274
-
301
+
275
302
  Raises:
276
303
  -------
277
304
  AssertionError
278
305
  If inj_amp is zero (must be non-zero to measure passive properties).
279
306
  """
280
- assert(inj_amp != 0)
281
- super().__init__(template_name=template_name, tstop=tstop,
282
- inj_amp=inj_amp, inj_delay=inj_delay, inj_dur=inj_dur, **kwargs)
307
+ assert inj_amp != 0
308
+ super().__init__(
309
+ template_name=template_name,
310
+ tstop=tstop,
311
+ inj_amp=inj_amp,
312
+ inj_delay=inj_delay,
313
+ inj_dur=inj_dur,
314
+ **kwargs,
315
+ )
283
316
  self.inj_stop = inj_delay + inj_dur
284
317
  self.method = method
285
- self.tau_methods = {'simple': self.tau_simple, 'exp2': self.tau_double_exponential, 'exp': self.tau_single_exponential}
318
+ self.tau_methods = {
319
+ "simple": self.tau_simple,
320
+ "exp2": self.tau_double_exponential,
321
+ "exp": self.tau_single_exponential,
322
+ }
286
323
 
287
324
  def tau_simple(self):
288
325
  """
289
326
  Calculate membrane time constant using the simple 0.632 criterion method.
290
-
327
+
291
328
  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
329
+ for the membrane potential to reach 63.2% (1-1/e) of its final value after
293
330
  a step current injection.
294
-
331
+
295
332
  Returns:
296
333
  --------
297
334
  callable
298
335
  A function that prints the calculation details when called.
299
-
336
+
300
337
  Notes:
301
338
  ------
302
339
  Sets the following attributes:
@@ -308,19 +345,22 @@ class Passive(CurrentClamp):
308
345
 
309
346
  def print_calc():
310
347
  print()
311
- print('Tau Calculation: time until 63.2% of dV')
312
- print('v_rest + 0.632*(v_final-v_rest)')
313
- print(f'{self.v_rest:.2f} + 0.632*({self.cell_v_final:.2f}-({self.v_rest:.2f})) = {v_t_const:.2f} (mV)')
314
- print(f'Time where V = {v_t_const:.2f} (mV) is {self.v_rest_time + self.tau:.2f} (ms)')
315
- print(f'{self.v_rest_time + self.tau:.2f} - {self.v_rest_time:g} = {self.tau:.2f} (ms)')
348
+ print("Tau Calculation: time until 63.2% of dV")
349
+ print("v_rest + 0.632*(v_final-v_rest)")
350
+ print(
351
+ f"{self.v_rest:.2f} + 0.632*({self.cell_v_final:.2f}-({self.v_rest:.2f})) = {v_t_const:.2f} (mV)"
352
+ )
353
+ print(f"Time where V = {v_t_const:.2f} (mV) is {self.v_rest_time + self.tau:.2f} (ms)")
354
+ print(f"{self.v_rest_time + self.tau:.2f} - {self.v_rest_time:g} = {self.tau:.2f} (ms)")
316
355
  print()
356
+
317
357
  return print_calc
318
358
 
319
359
  @staticmethod
320
360
  def single_exponential(t, a0, a, tau):
321
361
  """
322
362
  Single exponential function for fitting membrane potential response.
323
-
363
+
324
364
  Parameters:
325
365
  -----------
326
366
  t : array-like
@@ -331,7 +371,7 @@ class Passive(CurrentClamp):
331
371
  Amplitude of the exponential component
332
372
  tau : float
333
373
  Time constant of the exponential decay
334
-
374
+
335
375
  Returns:
336
376
  --------
337
377
  array-like
@@ -342,18 +382,18 @@ class Passive(CurrentClamp):
342
382
  def tau_single_exponential(self):
343
383
  """
344
384
  Calculate membrane time constant by fitting a single exponential curve.
345
-
385
+
346
386
  This method:
347
387
  1. Identifies the peak response (for sag characterization)
348
388
  2. Falls back to simple method for initial estimate
349
389
  3. Fits a single exponential function to the membrane potential response
350
390
  4. Sets tau to the exponential time constant
351
-
391
+
352
392
  Returns:
353
393
  --------
354
394
  callable
355
395
  A function that prints the calculation details when called.
356
-
396
+
357
397
  Notes:
358
398
  ------
359
399
  Sets the following attributes:
@@ -376,33 +416,44 @@ class Passive(CurrentClamp):
376
416
 
377
417
  p0 = (self.v_diff, self.tau) # initial estimate
378
418
  v0 = self.v_rest
419
+
379
420
  def fit_func(t, a, tau):
380
421
  return self.single_exponential(t, a0=v0 - a, a=a, tau=tau)
422
+
381
423
  bounds = ((-np.inf, 1e-3), np.inf)
382
- popt, self.pcov = curve_fit(fit_func, self.t_vec_inj, self.v_vec_inj, p0=p0, bounds=bounds, maxfev=10000)
424
+ popt, self.pcov = curve_fit(
425
+ fit_func, self.t_vec_inj, self.v_vec_inj, p0=p0, bounds=bounds, maxfev=10000
426
+ )
383
427
  self.popt = np.insert(popt, 0, v0 - popt[0])
384
428
  self.tau = self.popt[2]
385
429
 
386
430
  def print_calc():
387
431
  print()
388
- print('Tau Calculation: Fit a single exponential curve to the membrane potential response')
389
- print('f(t) = a0 + a*exp(-t/tau)')
390
- print(f'Fit parameters: (a0, a, tau) = ({self.popt[0]:.2f}, {self.popt[1]:.2f}, {self.popt[2]:.2f})')
391
- print(f'Membrane time constant is determined from the exponential term: {self.tau:.2f} (ms)')
432
+ print(
433
+ "Tau Calculation: Fit a single exponential curve to the membrane potential response"
434
+ )
435
+ print("f(t) = a0 + a*exp(-t/tau)")
436
+ print(
437
+ f"Fit parameters: (a0, a, tau) = ({self.popt[0]:.2f}, {self.popt[1]:.2f}, {self.popt[2]:.2f})"
438
+ )
439
+ print(
440
+ f"Membrane time constant is determined from the exponential term: {self.tau:.2f} (ms)"
441
+ )
392
442
  print()
393
- print('Sag potential: v_sag = v_peak - v_final = %.2f (mV)' % self.v_sag)
394
- print('Normalized sag potential: v_sag / (v_peak - v_rest) = %.3f' % self.sag_norm)
443
+ print("Sag potential: v_sag = v_peak - v_final = %.2f (mV)" % self.v_sag)
444
+ print("Normalized sag potential: v_sag / (v_peak - v_rest) = %.3f" % self.sag_norm)
395
445
  print()
446
+
396
447
  return print_calc
397
448
 
398
449
  @staticmethod
399
450
  def double_exponential(t, a0, a1, a2, tau1, tau2):
400
451
  """
401
452
  Double exponential function for fitting membrane potential response.
402
-
453
+
403
454
  This function is particularly useful for modeling cells with sag responses,
404
455
  where the membrane potential shows two distinct time constants.
405
-
456
+
406
457
  Parameters:
407
458
  -----------
408
459
  t : array-like
@@ -417,7 +468,7 @@ class Passive(CurrentClamp):
417
468
  Time constant of the first exponential component
418
469
  tau2 : float
419
470
  Time constant of the second exponential component
420
-
471
+
421
472
  Returns:
422
473
  --------
423
474
  array-like
@@ -428,15 +479,15 @@ class Passive(CurrentClamp):
428
479
  def tau_double_exponential(self):
429
480
  """
430
481
  Calculate membrane time constant by fitting a double exponential curve.
431
-
482
+
432
483
  This method is useful for cells with sag responses that cannot be
433
484
  fitted well with a single exponential.
434
-
485
+
435
486
  Returns:
436
487
  --------
437
488
  callable
438
489
  A function that prints the calculation details when called.
439
-
490
+
440
491
  Notes:
441
492
  ------
442
493
  Sets the following attributes:
@@ -458,30 +509,43 @@ class Passive(CurrentClamp):
458
509
  self.tau_simple()
459
510
  p0 = (self.v_sag, -self.v_max_diff, self.t_peak, self.tau) # initial estimate
460
511
  v0 = self.v_rest
512
+
461
513
  def fit_func(t, a1, a2, tau1, tau2):
462
514
  return self.double_exponential(t, v0 - a1 - a2, a1, a2, tau1, tau2)
515
+
463
516
  bounds = ((-np.inf, -np.inf, 1e-3, 1e-3), np.inf)
464
- popt, self.pcov = curve_fit(fit_func, self.t_vec_inj, self.v_vec_inj, p0=p0, bounds=bounds, maxfev=10000)
517
+ popt, self.pcov = curve_fit(
518
+ fit_func, self.t_vec_inj, self.v_vec_inj, p0=p0, bounds=bounds, maxfev=10000
519
+ )
465
520
  self.popt = np.insert(popt, 0, v0 - sum(popt[:2]))
466
521
  self.tau = max(self.popt[-2:])
467
522
 
468
523
  def print_calc():
469
524
  print()
470
- print('Tau Calculation: Fit a double exponential curve to the membrane potential response')
471
- print('f(t) = a0 + a1*exp(-t/tau1) + a2*exp(-t/tau2)')
472
- print('Constrained by initial value: f(0) = a0 + a1 + a2 = v_rest')
473
- print('Fit parameters: (a0, a1, a2, tau1, tau2) = (' + ', '.join(f'{x:.2f}' for x in self.popt) + ')')
474
- print(f'Membrane time constant is determined from the slowest exponential term: {self.tau:.2f} (ms)')
525
+ print(
526
+ "Tau Calculation: Fit a double exponential curve to the membrane potential response"
527
+ )
528
+ print("f(t) = a0 + a1*exp(-t/tau1) + a2*exp(-t/tau2)")
529
+ print("Constrained by initial value: f(0) = a0 + a1 + a2 = v_rest")
530
+ print(
531
+ "Fit parameters: (a0, a1, a2, tau1, tau2) = ("
532
+ + ", ".join(f"{x:.2f}" for x in self.popt)
533
+ + ")"
534
+ )
535
+ print(
536
+ f"Membrane time constant is determined from the slowest exponential term: {self.tau:.2f} (ms)"
537
+ )
475
538
  print()
476
- print('Sag potential: v_sag = v_peak - v_final = %.2f (mV)' % self.v_sag)
477
- print('Normalized sag potential: v_sag / (v_peak - v_rest) = %.3f' % self.sag_norm)
539
+ print("Sag potential: v_sag = v_peak - v_final = %.2f (mV)" % self.v_sag)
540
+ print("Normalized sag potential: v_sag / (v_peak - v_rest) = %.3f" % self.sag_norm)
478
541
  print()
542
+
479
543
  return print_calc
480
544
 
481
545
  def double_exponential_fit(self):
482
546
  """
483
547
  Get the double exponential fit values for plotting.
484
-
548
+
485
549
  Returns:
486
550
  --------
487
551
  tuple
@@ -492,11 +556,11 @@ class Passive(CurrentClamp):
492
556
  t_vec = self.v_rest_time + self.t_vec_inj
493
557
  v_fit = self.double_exponential(self.t_vec_inj, *self.popt)
494
558
  return t_vec, v_fit
495
-
559
+
496
560
  def single_exponential_fit(self):
497
561
  """
498
562
  Get the single exponential fit values for plotting.
499
-
563
+
500
564
  Returns:
501
565
  --------
502
566
  tuple
@@ -511,19 +575,19 @@ class Passive(CurrentClamp):
511
575
  def execute(self):
512
576
  """
513
577
  Run the simulation and calculate passive membrane properties.
514
-
578
+
515
579
  This method:
516
580
  1. Runs the NEURON simulation
517
581
  2. Extracts membrane potential at rest and steady-state
518
582
  3. Calculates input resistance from the step response
519
583
  4. Calculates membrane time constant using the specified method
520
584
  5. Prints detailed calculations for educational purposes
521
-
585
+
522
586
  Returns:
523
587
  --------
524
588
  tuple
525
589
  (time_vector, voltage_vector) from the simulation
526
-
590
+
527
591
  Notes:
528
592
  ------
529
593
  Sets several attributes including:
@@ -553,28 +617,42 @@ class Passive(CurrentClamp):
553
617
  print_calc = self.tau_methods.get(self.method, self.tau_simple)()
554
618
 
555
619
  print()
556
- print(f'V Rest: {self.v_rest:.2f} (mV)')
557
- print(f'Resistance: {self.r_in:.2f} (MOhms)')
558
- print(f'Membrane time constant: {self.tau:.2f} (ms)')
620
+ print(f"V Rest: {self.v_rest:.2f} (mV)")
621
+ print(f"Resistance: {self.r_in:.2f} (MOhms)")
622
+ print(f"Membrane time constant: {self.tau:.2f} (ms)")
559
623
  print()
560
- print(f'V_rest Calculation: Voltage taken at time {self.v_rest_time:.1f} (ms) is')
561
- print(f'{self.v_rest:.2f} (mV)')
624
+ print(f"V_rest Calculation: Voltage taken at time {self.v_rest_time:.1f} (ms) is")
625
+ print(f"{self.v_rest:.2f} (mV)")
562
626
  print()
563
- print('R_in Calculation: dV/dI = (v_final-v_rest)/(i_final-i_start)')
564
- print(f'({self.cell_v_final:.2f} - ({self.v_rest:.2f})) / ({self.inj_amp:g} - 0)')
565
- print(f'{np.sign(self.inj_amp) * self.v_diff:.2f} (mV) / {np.abs(self.inj_amp)} (nA) = {self.r_in:.2f} (MOhms)')
627
+ print("R_in Calculation: dV/dI = (v_final-v_rest)/(i_final-i_start)")
628
+ print(f"({self.cell_v_final:.2f} - ({self.v_rest:.2f})) / ({self.inj_amp:g} - 0)")
629
+ print(
630
+ f"{np.sign(self.inj_amp) * self.v_diff:.2f} (mV) / {np.abs(self.inj_amp)} (nA) = {self.r_in:.2f} (MOhms)"
631
+ )
566
632
  print_calc()
567
633
 
568
634
  return self.t_vec.to_python(), self.v_vec.to_python()
569
635
 
570
636
 
571
637
  class FI(object):
572
- def __init__(self, template_name, post_init_function=None,
573
- i_start=0., i_stop=1050., i_increment=100., tstart=50., tdur=1000., threshold=0.,
574
- record_sec='soma', record_loc=0.5, inj_sec='soma', inj_loc=0.5):
638
+ def __init__(
639
+ self,
640
+ template_name,
641
+ post_init_function=None,
642
+ i_start=0.0,
643
+ i_stop=1050.0,
644
+ i_increment=100.0,
645
+ tstart=50.0,
646
+ tdur=1000.0,
647
+ threshold=0.0,
648
+ record_sec="soma",
649
+ record_loc=0.5,
650
+ inj_sec="soma",
651
+ inj_loc=0.5,
652
+ ):
575
653
  """
576
654
  Initialize a frequency-current (F-I) curve simulation environment.
577
-
655
+
578
656
  Parameters:
579
657
  -----------
580
658
  template_name : str or callable
@@ -602,16 +680,18 @@ class FI(object):
602
680
  Section for current injection. Same format as record_sec. Default is 'soma'.
603
681
  inj_loc : float, optional
604
682
  Location (0-1) within section for current injection. Default is 0.5.
605
-
683
+
606
684
  Notes:
607
685
  ------
608
686
  This class creates multiple instances of the cell model, one for each
609
687
  current amplitude to be tested, allowing all simulations to be run
610
688
  in a single call to NEURON's run() function.
611
689
  """
612
- self.create_cell = getattr(h, template_name) if isinstance(template_name, str) else template_name
690
+ self.create_cell = (
691
+ getattr(h, template_name) if isinstance(template_name, str) else template_name
692
+ )
613
693
  self.post_init_function = post_init_function
614
- self.i_start = i_start * 1e-3 # pA to nA
694
+ self.i_start = i_start * 1e-3 # pA to nA
615
695
  self.i_stop = i_stop * 1e-3
616
696
  self.i_increment = i_increment * 1e-3
617
697
  self.tstart = tstart
@@ -644,26 +724,26 @@ class FI(object):
644
724
  def setup(self):
645
725
  """
646
726
  Set up the simulation environment for frequency-current (F-I) analysis.
647
-
727
+
648
728
  For each current amplitude to be tested, this method:
649
729
  1. Creates a current source at the injection site
650
730
  2. Sets up spike detection at the recording site
651
731
  3. Creates vectors to record spike times
652
-
732
+
653
733
  Notes:
654
734
  ------
655
735
  This preparation allows multiple simulations to be run with different
656
736
  current amplitudes in a single call to h.run().
657
737
  """
658
738
  for cell, amp in zip(self.cells, self.amps):
659
- inj_seg, _ = get_target_site(cell, self.inj_sec, self.inj_loc, 'injection')
739
+ inj_seg, _ = get_target_site(cell, self.inj_sec, self.inj_loc, "injection")
660
740
  src = h.IClamp(inj_seg)
661
741
  src.delay = self.tstart
662
742
  src.dur = self.tdur
663
743
  src.amp = amp
664
744
  self.sources.append(src)
665
745
 
666
- rec_seg, rec_sec = get_target_site(cell, self.record_sec, self.record_loc, 'recording')
746
+ rec_seg, rec_sec = get_target_site(cell, self.record_sec, self.record_loc, "recording")
667
747
  nc = h.NetCon(rec_seg._ref_v, None, sec=rec_sec)
668
748
  nc.threshold = self.threshold
669
749
  spvec = h.Vector()
@@ -671,18 +751,18 @@ class FI(object):
671
751
  self.ncs.append(nc)
672
752
  self.tspk_vecs.append(spvec)
673
753
 
674
- print(f'Injection location: {inj_seg}')
675
- print(f'Recording: {rec_seg}._ref_v')
754
+ print(f"Injection location: {inj_seg}")
755
+ print(f"Recording: {rec_seg}._ref_v")
676
756
 
677
757
  def execute(self):
678
758
  """
679
759
  Run the simulation and count spikes for each current amplitude.
680
-
760
+
681
761
  This method:
682
762
  1. Initializes and runs a single NEURON simulation that evaluates all current amplitudes
683
763
  2. Counts spikes for each current amplitude
684
764
  3. Prints a summary of results in tabular format
685
-
765
+
686
766
  Returns:
687
767
  --------
688
768
  tuple
@@ -699,22 +779,32 @@ class FI(object):
699
779
  print()
700
780
  print("Results")
701
781
  # lets make a df so the results line up nice
702
- data = {'Injection (nA):':self.amps,'number of spikes':self.nspks}
782
+ data = {"Injection (nA):": self.amps, "number of spikes": self.nspks}
703
783
  df = pd.DataFrame(data)
704
784
  print(df)
705
- #print(f'Injection (nA): ' + ', '.join(f'{x:g}' for x in self.amps))
706
- #print(f'Number of spikes: ' + ', '.join(f'{x:d}' for x in self.nspks))
785
+ # print(f'Injection (nA): ' + ', '.join(f'{x:g}' for x in self.amps))
786
+ # print(f'Number of spikes: ' + ', '.join(f'{x:d}' for x in self.nspks))
707
787
  print()
708
788
 
709
789
  return self.amps, self.nspks
710
790
 
711
791
 
712
792
  class ZAP(CurrentClamp):
713
- def __init__(self, template_name, inj_amp=100., inj_delay=200., inj_dur=15000.,
714
- tstop=15500., fstart=0., fend=15., chirp_type=None, **kwargs):
793
+ def __init__(
794
+ self,
795
+ template_name,
796
+ inj_amp=100.0,
797
+ inj_delay=200.0,
798
+ inj_dur=15000.0,
799
+ tstop=15500.0,
800
+ fstart=0.0,
801
+ fend=15.0,
802
+ chirp_type=None,
803
+ **kwargs,
804
+ ):
715
805
  """
716
806
  Initialize a ZAP (impedance amplitude profile) simulation environment.
717
-
807
+
718
808
  Parameters:
719
809
  -----------
720
810
  template_name : str or callable
@@ -736,35 +826,41 @@ class ZAP(CurrentClamp):
736
826
  Type of chirp current determining how frequency increases over time:
737
827
  - 'linear': Linear increase in frequency (default if None)
738
828
  - 'exponential': Exponential increase in frequency
739
- **kwargs :
829
+ **kwargs :
740
830
  Additional keyword arguments to pass to the parent CurrentClamp constructor.
741
-
831
+
742
832
  Notes:
743
833
  ------
744
834
  This class is designed for measuring the frequency-dependent impedance profile
745
835
  of a neuron using a chirp current that sweeps through frequencies.
746
-
836
+
747
837
  Raises:
748
838
  -------
749
839
  AssertionError
750
840
  - If inj_amp is zero
751
841
  - If chirp_type is 'exponential' and either fstart or fend is <= 0
752
842
  """
753
- assert(inj_amp != 0)
754
- super().__init__(template_name=template_name, tstop=tstop,
755
- inj_amp=inj_amp, inj_delay=inj_delay, inj_dur=inj_dur, **kwargs)
843
+ assert inj_amp != 0
844
+ super().__init__(
845
+ template_name=template_name,
846
+ tstop=tstop,
847
+ inj_amp=inj_amp,
848
+ inj_delay=inj_delay,
849
+ inj_dur=inj_dur,
850
+ **kwargs,
851
+ )
756
852
  self.inj_stop = inj_delay + inj_dur
757
853
  self.fstart = fstart
758
854
  self.fend = fend
759
855
  self.chirp_type = chirp_type
760
- self.chirp_func = {'linear': self.linear_chirp, 'exponential': self.exponential_chirp}
761
- if chirp_type=='exponential':
762
- assert(fstart > 0 and fend > 0)
856
+ self.chirp_func = {"linear": self.linear_chirp, "exponential": self.exponential_chirp}
857
+ if chirp_type == "exponential":
858
+ assert fstart > 0 and fend > 0
763
859
 
764
860
  def linear_chirp(self, t, f0, f1):
765
861
  """
766
862
  Generate a chirp current with linearly increasing frequency.
767
-
863
+
768
864
  Parameters:
769
865
  -----------
770
866
  t : ndarray
@@ -773,11 +869,11 @@ class ZAP(CurrentClamp):
773
869
  Start frequency (kHz)
774
870
  f1 : float
775
871
  End frequency (kHz)
776
-
872
+
777
873
  Returns:
778
874
  --------
779
875
  ndarray
780
- Current values with amplitude self.inj_amp and frequency
876
+ Current values with amplitude self.inj_amp and frequency
781
877
  increasing linearly from f0 to f1 Hz over time t
782
878
  """
783
879
  return self.inj_amp * np.sin(np.pi * (2 * f0 + (f1 - f0) / t[-1] * t) * t)
@@ -785,7 +881,7 @@ class ZAP(CurrentClamp):
785
881
  def exponential_chirp(self, t, f0, f1):
786
882
  """
787
883
  Generate a chirp current with exponentially increasing frequency.
788
-
884
+
789
885
  Parameters:
790
886
  -----------
791
887
  t : ndarray
@@ -794,13 +890,13 @@ class ZAP(CurrentClamp):
794
890
  Start frequency (kHz), must be > 0
795
891
  f1 : float
796
892
  End frequency (kHz), must be > 0
797
-
893
+
798
894
  Returns:
799
895
  --------
800
896
  ndarray
801
- Current values with amplitude self.inj_amp and frequency
897
+ Current values with amplitude self.inj_amp and frequency
802
898
  increasing exponentially from f0 to f1 Hz over time t
803
-
899
+
804
900
  Notes:
805
901
  ------
806
902
  For exponential chirp, both f0 and f1 must be positive.
@@ -811,12 +907,12 @@ class ZAP(CurrentClamp):
811
907
  def zap_current(self):
812
908
  """
813
909
  Create a frequency-modulated (chirp) current for probing impedance.
814
-
910
+
815
911
  This method:
816
912
  1. Sets up time vectors for the simulation and current injection
817
913
  2. Creates a chirp current based on the specified parameters (linear or exponential)
818
914
  3. Prepares the current vector for NEURON playback
819
-
915
+
820
916
  Notes:
821
917
  ------
822
918
  The chirp current increases in frequency from fstart to fend Hz over the duration
@@ -828,13 +924,13 @@ class ZAP(CurrentClamp):
828
924
  self.index_v_final = int(self.inj_stop / dt)
829
925
 
830
926
  t = np.arange(int(self.tstop / dt) + 1) * dt
831
- t_inj = t[:self.index_v_final - self.index_v_rest + 1]
832
- f0 = self.fstart * 1e-3 # Hz to 1/ms
927
+ t_inj = t[: self.index_v_final - self.index_v_rest + 1]
928
+ f0 = self.fstart * 1e-3 # Hz to 1/ms
833
929
  f1 = self.fend * 1e-3
834
930
  chirp_func = self.chirp_func.get(self.chirp_type, self.linear_chirp)
835
931
  self.zap_vec_inj = chirp_func(t_inj, f0, f1)
836
932
  i_inj = np.zeros_like(t)
837
- i_inj[self.index_v_rest:self.index_v_final + 1] = self.zap_vec_inj
933
+ i_inj[self.index_v_rest : self.index_v_final + 1] = self.zap_vec_inj
838
934
 
839
935
  self.zap_vec = h.Vector()
840
936
  self.zap_vec.from_python(i_inj)
@@ -843,50 +939,52 @@ class ZAP(CurrentClamp):
843
939
  def get_impedance(self, smooth=1):
844
940
  """
845
941
  Calculate and extract the frequency-dependent impedance profile.
846
-
942
+
847
943
  This method:
848
944
  1. Filters the impedance to the frequency range of interest
849
945
  2. Optionally applies smoothing to reduce noise
850
946
  3. Identifies the resonant frequency (peak impedance)
851
-
947
+
852
948
  Parameters:
853
949
  -----------
854
950
  smooth : int, optional
855
951
  Window size for smoothing the impedance. Default is 1 (no smoothing).
856
-
952
+
857
953
  Returns:
858
954
  --------
859
955
  tuple
860
956
  (frequencies, impedance_values) in the range of interest
861
-
957
+
862
958
  Notes:
863
959
  ------
864
960
  Sets self.peak_freq to the resonant frequency (frequency of maximum impedance).
865
961
  """
866
- f_idx = (self.freq > min(self.fstart, self.fend)) & (self.freq < max(self.fstart, self.fend))
962
+ f_idx = (self.freq > min(self.fstart, self.fend)) & (
963
+ self.freq < max(self.fstart, self.fend)
964
+ )
867
965
  impedance = self.impedance
868
966
  if smooth > 1:
869
- impedance = np.convolve(impedance, np.ones(smooth) / smooth, mode='same')
967
+ impedance = np.convolve(impedance, np.ones(smooth) / smooth, mode="same")
870
968
  freq, impedance = self.freq[f_idx], impedance[f_idx]
871
969
  self.peak_freq = freq[np.argmax(impedance)]
872
- print(f'Resonant Peak Frequency: {self.peak_freq:.3g} (Hz)')
970
+ print(f"Resonant Peak Frequency: {self.peak_freq:.3g} (Hz)")
873
971
  return freq, impedance
874
972
 
875
973
  def execute(self) -> Tuple[list, list]:
876
974
  """
877
975
  Run the ZAP simulation and calculate the impedance profile.
878
-
976
+
879
977
  This method:
880
978
  1. Sets up the chirp current
881
979
  2. Runs the NEURON simulation
882
980
  3. Calculates the impedance using FFT
883
981
  4. Prints a summary of the frequency range and analysis method
884
-
982
+
885
983
  Returns:
886
984
  --------
887
985
  tuple
888
986
  (time_vector, voltage_vector) from the simulation
889
-
987
+
890
988
  Notes:
891
989
  ------
892
990
  Sets several attributes including:
@@ -909,21 +1007,26 @@ class ZAP(CurrentClamp):
909
1007
  self.t_vec_inj = np.array(self.t_vec)[t_idx] - self.v_rest_time
910
1008
 
911
1009
  self.cell_v_amp_max = np.abs(self.v_vec_inj).max()
912
- self.Z = np.fft.rfft(self.v_vec_inj) / np.fft.rfft(self.zap_vec_inj) # MOhms
913
- self.freq = np.fft.rfftfreq(self.zap_vec_inj.size, d=self.dt * 1e-3) # ms to sec
1010
+ self.Z = np.fft.rfft(self.v_vec_inj) / np.fft.rfft(self.zap_vec_inj) # MOhms
1011
+ self.freq = np.fft.rfftfreq(self.zap_vec_inj.size, d=self.dt * 1e-3) # ms to sec
914
1012
  self.impedance = np.abs(self.Z)
915
1013
 
916
1014
  print()
917
- print('Chirp current injection with frequency changing from '
918
- f'{self.fstart:g} to {self.fend:g} Hz over {self.inj_dur * 1e-3:g} seconds')
919
- print('Impedance is calculated as the ratio of FFT amplitude '
920
- 'of membrane voltage to FFT amplitude of chirp current')
1015
+ print(
1016
+ "Chirp current injection with frequency changing from "
1017
+ f"{self.fstart:g} to {self.fend:g} Hz over {self.inj_dur * 1e-3:g} seconds"
1018
+ )
1019
+ print(
1020
+ "Impedance is calculated as the ratio of FFT amplitude "
1021
+ "of membrane voltage to FFT amplitude of chirp current"
1022
+ )
921
1023
  print()
922
1024
  return self.t_vec.to_python(), self.v_vec.to_python()
923
1025
 
924
1026
 
925
- class Profiler():
1027
+ class Profiler:
926
1028
  """All in one single cell profiler"""
1029
+
927
1030
  def __init__(self, template_dir: str = None, mechanism_dir: str = None, dt=None):
928
1031
  self.template_dir = None
929
1032
  self.mechanism_dir = None
@@ -942,8 +1045,12 @@ class Profiler():
942
1045
  h.steps_per_ms = 1 / h.dt
943
1046
 
944
1047
  def load_templates(self, hoc_template_file=None):
945
- if self.templates is None: # Can really only do this once
946
- if self.mechanism_dir != './' and self.mechanism_dir != '.' and self.mechanism_dir != '././':
1048
+ if self.templates is None: # Can really only do this once
1049
+ if (
1050
+ self.mechanism_dir != "./"
1051
+ and self.mechanism_dir != "."
1052
+ and self.mechanism_dir != "././"
1053
+ ):
947
1054
  neuron.load_mechanisms(self.mechanism_dir)
948
1055
  h_base = set(dir(h))
949
1056
 
@@ -966,9 +1073,16 @@ class Profiler():
966
1073
 
967
1074
  return self.templates
968
1075
 
969
- def passive_properties(self, template_name: str, post_init_function: str = None,
970
- record_sec: str = 'soma', inj_sec: str = 'soma', plot: bool = True,
971
- method=None, **kwargs) -> Tuple[list, list]:
1076
+ def passive_properties(
1077
+ self,
1078
+ template_name: str,
1079
+ post_init_function: str = None,
1080
+ record_sec: str = "soma",
1081
+ inj_sec: str = "soma",
1082
+ plot: bool = True,
1083
+ method=None,
1084
+ **kwargs,
1085
+ ) -> Tuple[list, list]:
972
1086
  """
973
1087
  Calculates passive properties for the specified cell template_name
974
1088
 
@@ -991,45 +1105,67 @@ class Profiler():
991
1105
  extra key word arguments for Passive()
992
1106
 
993
1107
  Returns time (ms), membrane voltage (mV)
994
- """
995
- passive = Passive(template_name, post_init_function=post_init_function,
996
- record_sec=record_sec, inj_sec=inj_sec, method=method, **kwargs)
1108
+ """
1109
+ passive = Passive(
1110
+ template_name,
1111
+ post_init_function=post_init_function,
1112
+ record_sec=record_sec,
1113
+ inj_sec=inj_sec,
1114
+ method=method,
1115
+ **kwargs,
1116
+ )
997
1117
  time, amp = passive.execute()
998
1118
 
999
1119
  if plot:
1000
1120
  plt.figure()
1001
1121
  plt.plot(time, amp)
1002
- if passive.method == 'exp2':
1003
- plt.plot(*passive.double_exponential_fit(), 'r:', label='double exponential fit')
1122
+ if passive.method == "exp2":
1123
+ plt.plot(*passive.double_exponential_fit(), "r:", label="double exponential fit")
1004
1124
  plt.legend()
1005
1125
  plt.title("Passive Cell Current Injection")
1006
- plt.xlabel('Time (ms)')
1007
- plt.ylabel('Membrane Potential (mV)')
1126
+ plt.xlabel("Time (ms)")
1127
+ plt.ylabel("Membrane Potential (mV)")
1008
1128
  plt.show()
1009
1129
 
1010
1130
  return time, amp
1011
1131
 
1012
- def current_injection(self, template_name: str, post_init_function: str = None,
1013
- record_sec: str = 'soma', inj_sec: str = 'soma', plot: bool = True,
1014
- **kwargs) -> Tuple[list, list]:
1015
-
1016
- ccl = CurrentClamp(template_name, post_init_function=post_init_function,
1017
- record_sec=record_sec, inj_sec=inj_sec, **kwargs)
1132
+ def current_injection(
1133
+ self,
1134
+ template_name: str,
1135
+ post_init_function: str = None,
1136
+ record_sec: str = "soma",
1137
+ inj_sec: str = "soma",
1138
+ plot: bool = True,
1139
+ **kwargs,
1140
+ ) -> Tuple[list, list]:
1141
+ ccl = CurrentClamp(
1142
+ template_name,
1143
+ post_init_function=post_init_function,
1144
+ record_sec=record_sec,
1145
+ inj_sec=inj_sec,
1146
+ **kwargs,
1147
+ )
1018
1148
  time, amp = ccl.execute()
1019
1149
 
1020
1150
  if plot:
1021
1151
  plt.figure()
1022
1152
  plt.plot(time, amp)
1023
1153
  plt.title("Current Injection")
1024
- plt.xlabel('Time (ms)')
1025
- plt.ylabel('Membrane Potential (mV)')
1154
+ plt.xlabel("Time (ms)")
1155
+ plt.ylabel("Membrane Potential (mV)")
1026
1156
  plt.show()
1027
1157
 
1028
1158
  return time, amp
1029
1159
 
1030
- def fi_curve(self, template_name: str, post_init_function: str = None,
1031
- record_sec: str = 'soma', inj_sec: str = 'soma', plot: bool = True,
1032
- **kwargs) -> Tuple[list, list]:
1160
+ def fi_curve(
1161
+ self,
1162
+ template_name: str,
1163
+ post_init_function: str = None,
1164
+ record_sec: str = "soma",
1165
+ inj_sec: str = "soma",
1166
+ plot: bool = True,
1167
+ **kwargs,
1168
+ ) -> Tuple[list, list]:
1033
1169
  """
1034
1170
  Calculates an FI curve for the specified cell template_name
1035
1171
 
@@ -1050,23 +1186,36 @@ class Profiler():
1050
1186
  Returns the injection amplitudes (nA) used, number of spikes per amplitude supplied
1051
1187
  list(amps), list(# of spikes)
1052
1188
  """
1053
- fi = FI(template_name, post_init_function=post_init_function,
1054
- record_sec=record_sec, inj_sec=inj_sec, **kwargs)
1189
+ fi = FI(
1190
+ template_name,
1191
+ post_init_function=post_init_function,
1192
+ record_sec=record_sec,
1193
+ inj_sec=inj_sec,
1194
+ **kwargs,
1195
+ )
1055
1196
  amp, nspk = fi.execute()
1056
1197
 
1057
1198
  if plot:
1058
1199
  plt.figure()
1059
1200
  plt.plot(amp, nspk)
1060
1201
  plt.title("FI Curve")
1061
- plt.xlabel('Injection (nA)')
1062
- plt.ylabel('# Spikes')
1202
+ plt.xlabel("Injection (nA)")
1203
+ plt.ylabel("# Spikes")
1063
1204
  plt.show()
1064
1205
 
1065
1206
  return amp, nspk
1066
1207
 
1067
- def impedance_amplitude_profile(self, template_name: str, post_init_function: str = None,
1068
- record_sec: str = 'soma', inj_sec: str = 'soma', plot: bool = True,
1069
- chirp_type=None, smooth: int = 9, **kwargs) -> Tuple[list, list]:
1208
+ def impedance_amplitude_profile(
1209
+ self,
1210
+ template_name: str,
1211
+ post_init_function: str = None,
1212
+ record_sec: str = "soma",
1213
+ inj_sec: str = "soma",
1214
+ plot: bool = True,
1215
+ chirp_type=None,
1216
+ smooth: int = 9,
1217
+ **kwargs,
1218
+ ) -> Tuple[list, list]:
1070
1219
  """
1071
1220
  chirp_type: str
1072
1221
  Type of chirp current (see ZAP)
@@ -1075,32 +1224,39 @@ class Profiler():
1075
1224
  **kwargs:
1076
1225
  extra key word arguments for ZAP()
1077
1226
  """
1078
- zap = ZAP(template_name, post_init_function=post_init_function,
1079
- record_sec=record_sec, inj_sec=inj_sec, chirp_type=chirp_type, **kwargs)
1227
+ zap = ZAP(
1228
+ template_name,
1229
+ post_init_function=post_init_function,
1230
+ record_sec=record_sec,
1231
+ inj_sec=inj_sec,
1232
+ chirp_type=chirp_type,
1233
+ **kwargs,
1234
+ )
1080
1235
  time, amp = zap.execute()
1081
1236
 
1082
1237
  if plot:
1083
1238
  plt.figure()
1084
1239
  plt.plot(time, amp)
1085
1240
  plt.title("ZAP Response")
1086
- plt.xlabel('Time (ms)')
1087
- plt.ylabel('Membrane Potential (mV)')
1241
+ plt.xlabel("Time (ms)")
1242
+ plt.ylabel("Membrane Potential (mV)")
1088
1243
 
1089
1244
  plt.figure()
1090
1245
  plt.plot(time, zap.zap_vec)
1091
- plt.title('ZAP Current')
1092
- plt.xlabel('Time (ms)')
1093
- plt.ylabel('Current Injection (nA)')
1246
+ plt.title("ZAP Current")
1247
+ plt.xlabel("Time (ms)")
1248
+ plt.ylabel("Current Injection (nA)")
1094
1249
 
1095
1250
  plt.figure()
1096
1251
  plt.plot(*zap.get_impedance(smooth=smooth))
1097
- plt.title('Impedance Amplitude Profile')
1098
- plt.xlabel('Frequency (Hz)')
1099
- plt.ylabel('Impedance (MOhms)')
1252
+ plt.title("Impedance Amplitude Profile")
1253
+ plt.xlabel("Frequency (Hz)")
1254
+ plt.ylabel("Impedance (MOhms)")
1100
1255
  plt.show()
1101
1256
 
1102
1257
  return time, amp
1103
1258
 
1259
+
1104
1260
  # Example usage
1105
1261
  # profiler = Profiler('./temp/templates', './temp/mechanisms/modfiles')
1106
1262
  # profiler.passive_properties('Cell_Cf')
@@ -1108,8 +1264,14 @@ class Profiler():
1108
1264
  # profiler.current_injection('Cell_Cf', post_init_function="insert_mechs(123)", inj_amp=300, inj_delay=100)
1109
1265
 
1110
1266
 
1111
- def run_and_plot(sim, title=None, xlabel='Time (ms)', ylabel='Membrane Potential (mV)',
1112
- plot=True, plot_injection_only=False):
1267
+ def run_and_plot(
1268
+ sim,
1269
+ title=None,
1270
+ xlabel="Time (ms)",
1271
+ ylabel="Membrane Potential (mV)",
1272
+ plot=True,
1273
+ plot_injection_only=False,
1274
+ ):
1113
1275
  """Helper function for running simulation and plot
1114
1276
  sim: instance of the simulation class in this module
1115
1277
  title, xlabel, ylabel: plot labels