-
Notifications
You must be signed in to change notification settings - Fork 1
/
current_injection.py
395 lines (343 loc) · 16.3 KB
/
current_injection.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
import os
import sys
import argparse as arg
import numpy as np
import matplotlib.pyplot as plt
from dlutils import cell as cu
from dlutils import utils as dlu
import pickle
import json
import time
def make_cell(swc_file, parameters, mechanisms, cell_name=None, replace_axon=False, add_axon_if_missing=True):
if cell_name is None:
import random
cell_name = 'cell_%06d' % random.randint(0,999999)
cell = cu.Cell(cell_name, swc_file, parameters, mechanisms)
cell.instantiate(replace_axon, add_axon_if_missing)
return cell
def make_recorders(cell, h, apical_dst=None, basal_dst=None, mech_vars=None):
recorders = {'spike_times': h.Vector()}
apc = h.APCount(cell.morpho.soma[0](0.5))
apc.thresh = -20.
apc.record(recorders['spike_times'])
for lbl in 't','soma.v':
recorders[lbl] = h.Vector()
recorders['t'].record(h._ref_t)
recorders['soma.v'].record(cell.morpho.soma[0](0.5)._ref_v)
try:
recorders['soma.cai'] = h.Vector()
recorders['soma.cai'].record(cell.morpho.soma[0](0.5)._ref_cai)
except:
pass
try:
recorders['soma.ica'] = h.Vector()
recorders['soma.ica'].record(cell.morpho.soma[0](0.5)._ref_ica)
except:
pass
if cell.n_axonal_sections > 0:
recorders['axon.v'] = h.Vector()
recorders['axon.v'].record(cell.morpho.axon[0](0.5)._ref_v)
else:
print('The cell has no axon.')
apical_seg = {}
basal_seg = {}
if apical_dst is not None and cell.n_apical_sections > 0:
h.distance(0, 0.5, sec=cell.morpho.soma[0])
for sec in cell.morpho.apic:
for seg in sec:
for k,v in apical_dst.items():
if not k+'.v' in recorders and h.distance(1, seg.x, sec=sec) >= v:
apical_seg[k] = seg
recorders[k+'.v'] = h.Vector()
recorders[k+'.v'].record(seg._ref_v)
try:
recorders[k+'.cai'] = h.Vector()
recorders[k+'.cai'].record(seg._ref_cai)
except:
pass
try:
recorders[k+'.ica'] = h.Vector()
recorders[k+'.ica'].record(seg._ref_ica)
except:
pass
print('Adding recorder on the apical dendrite at a distance of {:.2f} um.'\
.format(h.distance(1, seg.x, sec=sec)))
if basal_dst is not None and cell.n_basal_sections > 0:
h.distance(0, 0.5, sec=cell.morpho.soma[0])
for sec in cell.morpho.basal:
for seg in sec:
for k,v in basal_dst.items():
if not k+'.v' in recorders and h.distance(1, seg.x, sec=sec) >= v:
basal_seg[k] = seg
recorders[k+'.v'] = h.Vector()
recorders[k+'.v'].record(seg._ref_v)
try:
recorders[k+'.cai'] = h.Vector()
recorders[k+'.cai'].record(seg._ref_cai)
except:
pass
try:
recorders[k+'.ica'] = h.Vector()
recorders[k+'.ica'].record(seg._ref_ica)
except:
pass
print('Adding recorder on the basal dendrite at a distance of {:.2f} um.'\
.format(h.distance(1, seg.x, sec=sec)))
if mech_vars is None:
return recorders,None
def make_rec(seg, cell, prefix, mech_var, name):
key = prefix + '.' + mech_var + '_' + name
rec = h.Vector()
rec.record(getattr(seg, '_ref_' + mech_var + '_' + name))
try:
gbar = getattr(seg, 'gbar_' + name)
except:
try:
gbar = getattr(seg, 'g' + name + 'bar_' + name)
except:
gbar = getattr(seg, 'g' + name[:2] + 'bar_' + name)
return rec,gbar,key
gbars = {}
for mech_name,var_name in mech_vars.items():
try:
rec,gbar,key = make_rec(cell.morpho.soma[0](0.5), cell, 'soma', var_name, mech_name)
recorders[key],gbars[key] = rec,gbar
except:
pass
for k,seg in apical_seg.items():
try:
rec,gbar,key = make_rec(seg, cell, k, var_name, mech_name)
recorders[key],gbars[key] = rec,gbar
except:
pass
for k,seg in basal_seg.items():
try:
rec,gbar,key = make_rec(seg, cell, k, var_name, mech_name)
recorders[key],gbars[key] = rec,gbar
except:
pass
return recorders,apc,gbars
def run_simulation(tstop, h, cell_name=None, verbose=False):
h.cvode_active(1)
h.tstop = tstop
fmt = lambda now: '%02d:%02d:%02d' % (now.tm_hour,now.tm_min,now.tm_sec)
start = time.time()
if verbose:
print('{}>> simulation started @ {}.'.format(cell_name,fmt(time.localtime(start))))
h.run()
stop = time.time()
if verbose:
print('{}>> simulation finished @ {}, elapsed time = {} seconds.'.format(
cell_name,fmt(time.localtime(stop)),stop-start))
def plot_results(recorders, gbars=None, apical_dst={}, basal_dst={}, x_lim=None):
apical_col = 'rm'
basal_col = 'gc'
fig,ax = plt.subplots(3, 1, sharex=True, figsize=(6,4))
t = np.array(recorders['t'])
for i,(k,v) in enumerate(apical_dst.items()):
ax[0].plot(t, recorders[k+'.v'], apical_col[i], label='Apical - {:.0f} um'.format(v))
ax[1].plot(t, np.array(recorders[k+'.cai'])*1e3, apical_col[i])
ax[2].plot(t, np.array(recorders[k+'.ica'])*1e3, apical_col[i])
for i,(k,v) in enumerate(basal_dst.items()):
ax[0].plot(t, recorders[k+'.v'], basal_col[i], label='Basal - {:.0f} um'.format(v))
ax[1].plot(t, np.array(recorders[k+'.cai'])*1e3, basal_col[i])
ax[2].plot(t, np.array(recorders[k+'.ica'])*1e3, basal_col[i])
ax[0].plot(t,recorders['soma.v'],'k',label='Soma')
ax[0].set_ylabel(r'$V_m$ (mV)')
ax[0].legend(loc='best')
ax[1].plot(t,np.array(recorders['soma.cai'])*1e3,'k')
ax[1].set_ylabel(r'$Ca_i$ ($\mu$M)')
ax[2].plot(t,np.array(recorders['soma.ica'])*1e3,'k')
ax[2].set_ylabel(r'$I_{Ca}$ (nA)')
ax[2].set_xlabel('Time (ms)')
if x_lim is not None:
ax[2].set_xlim(x_lim)
plt.savefig('step.pdf')
if gbars is not None:
fig = None
for name,rec in recorders.items():
if ('soma' in name or 'apic' in name or 'basal' in name or 'axon' in name) \
and not name.split('.')[1] in ('v','cai','ica'):
if fig is None:
fig,ax = plt.subplots(1, 1, figsize=(6,4))
ax.plot(recorders['t'], np.array(rec)/gbars[name], label=name)
if fig is not None:
ax.legend(loc='best')
if x_lim is not None:
ax.set_xlim(x_lim)
plt.show()
def inject_current_step(I, delay, dur, swc_file, inj_loc, inj_dist, parameters, mechanisms, after=100, N=1, freq=np.inf,
apical_dst={}, basal_dst={}, current_recordings='', replace_axon=False, add_axon_if_missing=True,
cell_name=None, neuron=None, do_plot=False, verbose=False):
if neuron is not None:
h = neuron.h
else:
from neuron import h
cell = make_cell(swc_file, parameters, mechanisms, cell_name, replace_axon, add_axon_if_missing)
if not isinstance(I,list):
I = [I for _ in range(N)]
elif len(I) == 1:
I = [I[0] for _ in range(N)]
if len(I) != N:
raise Exception('Number of stimuli does not agree with number of current values')
if inj_loc == 'soma':
inj_seg = cell.morpho.soma[0](0.5)
else:
inj_seg = None
h.distance(0, 0.5, sec=cell.morpho.soma[0])
if inj_loc == 'apical':
sections = cell.morpho.apic
elif inj_loc == 'basal':
sections = cell.morpho.basal
elif inj_loc == 'axon':
sections = cell.morpho.axon
else:
raise Exception('Unknown group "{}"'.format(inj_loc))
for sec in sections:
for seg in sec:
if h.distance(1, seg.x, sec=sec) >= inj_dist:
inj_seg = seg
print('Adding stimulus on "{}" at a distance of {:.2f} um.'\
.format(inj_loc, h.distance(1, seg.x, sec=sec)))
break
if inj_seg is not None:
break
if inj_seg is None:
raise Exception('No segment on "{}" at a distance of {:.2f} um'.format(inj_loc, inj_dist))
stimuli = [h.IClamp(inj_seg) for _ in range(N)]
for i,(stim,amp) in enumerate(zip(stimuli,I)):
stim.delay = delay + i/freq*1000
stim.dur = dur
stim.amp = amp*1e-3
CA3_mech_vars = {'kca': 'gk', 'kap': 'gka', 'cat': 'gcat', 'cal': 'gcal', 'can': 'gcan', \
'cagk': 'gkca', 'kad': 'gka'}
CTX_mech_vars = {'Ca_HVA': 'gCa_HVA', 'Ca_LVAst': 'gCa_LVAst'}
if current_recordings == '':
mech_vars = {}
elif current_recordings == 'CA3':
mech_vars = CTX_mech_vars
elif current_recordings == 'CTX':
mech_vars = CTX_mech_vars
else:
raise Exception('current_recordings must be either "CA3" or "CTX"')
recorders,apc,gbars = make_recorders(cell, h, apical_dst, basal_dst, mech_vars)
if current_recordings == 'CA3':
try:
rec = h.Vector()
rec.record(cell.morpho.soma[0](0.5)._ref_m_kmb)
recorders['soma.m_kmb'] = rec
gbars['soma.m_kmb'] = 1
except:
pass
t_end = dur + (N-1) / freq * 1000 + delay + after
run_simulation(t_end, h, cell.cell_name, verbose)
if do_plot:
plot_results(recorders, gbars, apical_dst, basal_dst, x_lim=[delay-50, t_end])
h('forall delete_section()')
return recorders
def main():
parser = arg.ArgumentParser(description='Compute the f-I curve of a neuron model.')
parser.add_argument('I', type=float, action='store', nargs='+', help='current value in pA')
parser.add_argument('-f','--swc-file', type=str, help='SWC file defining the cell morphology', required=True)
parser.add_argument('-p','--params-file', type=str, default=None,
help='JSON file containing the parameters of the model', required=True)
parser.add_argument('-m','--mech-file', type=str, default=None,
help='JSON file containing the mechanisms to be inserted into the cell')
parser.add_argument('-c','--config-file', type=str, default=None,
help='JSON file containing the configuration of the model')
parser.add_argument('-n','--cell-name', type=str, default=None,
help='name of the cell as it appears in the configuration file')
parser.add_argument('-R','--replace-axon', type=str, default=None,
help='whether to replace the axon (accepted values: "yes" or "no")')
parser.add_argument('-A', '--add-axon-if-missing', type=str, default=None,
help='whether add an axon if the cell does not have one (accepted values: "yes" or "no")')
parser.add_argument('-o','--output', type=str, default='step.pkl', help='output file name (default: step.pkl)')
parser.add_argument('--plot', action='store_true', help='show a plot (default: no)')
parser.add_argument('-v', '--verbose', action='store_true', help='be verbose (default: no)')
parser.add_argument('--dur', required=True, type=float, help='stimulus duration')
parser.add_argument('--delay', default=1000., type=float, help='delay before stimulus onset (default: 1000 ms)')
parser.add_argument('-N','--nsteps', default=1, type=int, help='number of current steps (default 1)')
parser.add_argument('-F','--frequency', default=np.inf, type=float, help='frequency of current steps (default +inf)')
parser.add_argument('--injection-site', default='soma', type=str, help='injection site (default soma)')
parser.add_argument('--injection-site-distance', default=0, type=float, help='injection distance (default 0 um)')
parser.add_argument('--basal-recordings', default='', type=str, help='Comma-separated recording distances along the basal dendrite')
parser.add_argument('--apical-recordings', default='', type=str, help='Comma-separated recording distances along the apical dendrite')
parser.add_argument('--current-recordings', default='', type=str,
help='Which current sets to record (default none, accepted values are "CA3" and "CTX")')
args = parser.parse_args(args=sys.argv[1:])
if args.nsteps < 1:
print('The number of current steps must be >= 1')
sys.exit(1)
if args.frequency <= 0:
print('The frequency of the current steps must be > 0')
sys.exit(2)
if not args.injection_site in ('soma','apical','basal','axon'):
print('Unknown injection site: accepted values are "soma", "apical", "basal" and "axon".')
sys.exit(3)
if args.injection_site_distance < 0:
print('Injection distance must be > 0.')
sys.exit(4)
if args.mech_file is not None and args.config_file is not None:
print('--mech-file and --config-file cannot both be present.')
sys.exit(5)
if args.config_file is not None and args.cell_name is None:
print('You must specify --cell-name with --config-file.')
sys.exit(6)
parameters = json.load(open(args.params_file,'r'))
if args.config_file is not None or args.cell_name is not None:
import dlutils
mechanisms = dlu.extract_mechanisms(args.config_file, args.cell_name)
else:
mechanisms = json.load(open(args.mech_file,'r'))
try:
sim_pars = pickle.load(open('simulation_parameters.pkl','rb'))
except:
sim_pars = None
if args.replace_axon is None:
if sim_pars is None:
replace_axon = False
else:
replace_axon = sim_pars['replace_axon']
print('Setting replace_axon = {} as per original optimization.'.format(replace_axon))
else:
if args.replace_axon.lower() in ('y','yes'):
replace_axon = True
elif args.replace_axon.lower() in ('n','no'):
replace_axon = False
else:
print('Unknown value for --replace-axon: "{}".'.format(args.replace_axon))
sys.exit(7)
if args.add_axon_if_missing is None:
if sim_pars is None:
add_axon_if_missing = True
else:
add_axon_if_missing = not sim_pars['no_add_axon']
print('Setting add_axon_if_missing = {} as per original optimization.'.format(add_axon_if_missing))
else:
if args.add_axon_if_missing.lower() in ('y','yes'):
add_axon_if_missing = True
elif args.add_axon_if_missing.lower() in ('n','no'):
add_axon_if_missing = False
else:
print('Unknown value for --add-axon-if-missing: "{}".'.format(args.add_axon_if_missing))
sys.exit(8)
if args.current_recordings.upper() not in ('', 'CA3', 'CTX'):
print('Accepted values for --current-recordings are "CA3" and "CTX".')
sys.exit(9)
if args.apical_recordings == '':
apical_dst = {}
else:
apical_dst = {'apic{}'.format(i+1): float(dst) for i,dst in enumerate(args.apical_recordings.split(','))}
if args.basal_recordings == '':
basal_dst = {}
else:
basal_dst = {'basal{}'.format(i+1): float(dst) for i,dst in enumerate(args.basal_recordings.split(','))}
rec = inject_current_step(args.I, args.delay, args.dur, args.swc_file, args.injection_site, \
args.injection_site_distance, parameters, mechanisms, args.delay, \
args.nsteps, args.frequency, apical_dst, basal_dst, \
args.current_recordings.upper(), replace_axon, add_axon_if_missing, \
do_plot=args.plot, verbose=args.verbose)
step = {'I': args.I, 'time': np.array(rec['t']), 'voltage': np.array(rec['soma.v']), 'spike_times': np.array(rec['spike_times'])}
pickle.dump(step, open(args.output,'wb'))
if __name__ == '__main__':
main()