Skip to content

Instantly share code, notes, and snippets.

@cjayb
Last active May 11, 2021 17:02
Show Gist options
  • Save cjayb/56a93385db003a009cac651516d299e2 to your computer and use it in GitHub Desktop.
Save cjayb/56a93385db003a009cac651516d299e2 to your computer and use it in GitHub Desktop.
# %%
# %matplotlib widget
import matplotlib.pyplot as plt
from matplotlib.colors import SymLogNorm
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
import numpy as np
from neuron import h
from hnn_core.network_builder import load_custom_mechanisms
from hnn_core.lfp import _LFPElectrode
from hnn_core.cells_default import pyramidal
def calc_monopole_multiplier(ele_pos, sec_mid, sigma=.3):
ele_pos = np.array(ele_pos) # electrode position
# distance from compartment to electrode
dis = np.linalg.norm(ele_pos - mid)
phi = 1. / dis
return 1000.0 * phi / (4.0 * np.pi * sigma)
_CVODE = h.CVode()
_CVODE.active(0)
_CVODE.use_fast_imem(1)
sigma = 0.3 # S / m
method = 'psa'
load_custom_mechanisms()
params = dict(tstop=300, dt=0.025, burnin=200)
h.load_file("stdrun.hoc")
h.tstop = params['tstop'] + params['burnin']
h.dt = params['dt']
h.celsius = 37
silence_hh = {'L5Pyr_soma_gkbar_hh2': 0.0,
'L5Pyr_soma_gnabar_hh2': 0.0,
'L5Pyr_dend_gkbar_hh2': 0.0,
'L5Pyr_dend_gnabar_hh2': 0.0}
l5p = pyramidal(pos=(0, 0, 0), cell_name='L5Pyr',
override_params=silence_hh)
# for sec in l5p.sections:
# sec.insert('pas')
# for seg in sec:
# seg.pas.g = 4.26e-05
# if 'soma' in sec.name():
# seg.pas.e = -65.
# else:
# seg.pas.e = -71.
def laminar_array(ymin, ymax, ystep, posx=10, posz=10):
el_array = list()
for posy in np.arange(ymin, ymax, ystep):
el_array.append(_LFPElectrode((posx, posy, posz), sigma=sigma, pc=None,
cvode=_CVODE, method=method))
return el_array
laminar_lfp = laminar_array(-200, 2000, 100)
# %%
ls = h.allsec()
allsecs = [s for s in ls]
imem_vecs = dict()
for ii, sec in enumerate(allsecs):
key = '_'.join(sec.name().split('_')[1:])
imem_vecs.update({key: list()})
for seg in sec.allseg():
imem_vecs[key].append(h.Vector().record(sec(seg.x)._ref_i_membrane_))
syn_deep = l5p.synapses['basal_1_nmda']
syn_superf = l5p.synapses['apical_2_nmda']
syn_deep_i = h.Vector().record(syn_deep._ref_i)
syn_superf_i = h.Vector().record(syn_superf._ref_i)
soma_v = l5p.rec_v.record(l5p.sections['soma'](0.5)._ref_v)
t = h.Vector().record(h._ref_t)
stim_deep = h.NetStim() # Make a new stimulator
stim_deep.number = 1
stim_deep.start = 49 + params['burnin'] # ms
ncstim_deep = h.NetCon(stim_deep, syn_deep)
ncstim_deep.delay = 10
ncstim_deep.weight[0] = 0.02 # NetCon weight is a vector.
stim_superf = h.NetStim() # Make a new stimulator
stim_superf.number = 2
stim_superf.start = 199 + params['burnin'] # ms
ncstim_superf = h.NetCon(stim_superf, syn_superf)
ncstim_superf.delay = 1
ncstim_superf.weight[0] = 0.02 # NetCon weight is a vector.
h.finitialize()
h.fcurrent()
h.run()
times = np.array(t.to_python())
v_soma = np.array(soma_v.to_python())
i_deep = np.array(syn_deep_i.to_python())
i_superf = np.array(syn_superf_i.to_python())
fig, ax = plt.subplots(2, 1, sharex=True)
ax[0].plot(times[times >= params['burnin']],
v_soma[times >= params['burnin']])
ax[1].plot(times[times >= params['burnin']],
i_deep[times >= params['burnin']],
label='$I_{deep}$')
ax[1].plot(times[times >= params['burnin']],
i_superf[times >= params['burnin']],
label='$I_{superf}$')
ax[0].set_title('Somatic membrane potential (mV)')
ax[1].legend(loc='lower right')
ax[1].set_title('Synaptic currents (nA)')
# laminar LFP
efig, eax = plt.subplots(1, 1)
times_lfp = np.array(laminar_lfp[0].lfp_t.to_python())
tind = times_lfp >= params['burnin']
cmap = plt.get_cmap('inferno')
depths = np.arange(-200, 2000, 100)
for ii, lfp in enumerate(laminar_lfp):
eax.plot(times_lfp[tind] + ii * 2,
10 * ii + 10 * np.array(lfp.lfp_v)[tind],
color=cmap(ii / len(laminar_lfp)))
eax.text(200, 10 * ii, f'{int(depths[ii])}')
eax.set_ylim((-20, len(depths) * 10))
eax.set_yticks(())
eax.set_title('NMDA events at 250 ms (basal_1) and 400 ms (apical_2)')
eax.set_ylabel('Potential as function of distance from soma')
eax.set_xlabel('Time (ms)')
# %% plot NET membrane current in each section
# NB summing over segments
sec_order = ['basal_3', 'basal_2', 'basal_1', 'soma',
'apical_trunk', 'apical_oblique',
'apical_1', 'apical_2', 'apical_tuft']
sec_order.reverse()
soma_idx = 3
fig, ax = plt.subplots(len(sec_order), 1, figsize=(8, 12))
for ii_sec, key in enumerate(sec_order):
for ii_seg, _h_seg_im in enumerate(imem_vecs[key]):
seg_im = np.array(_h_seg_im.to_python())[:-1]
ax[ii_sec].plot(times_lfp[tind] + ii_seg * 10,
ii_seg + 10 * seg_im[tind])
# ax[ii_sec].text(150, im, key)
ax[ii_sec].set_ylabel(key)
ax[ii_sec].set_ylim((-0.2, (ii_seg + 1) + 0.2))
ax[ii_sec].set_xlabel('Time (ms)')
fig.suptitle('Transmembrane current (nA) in sub-segments')
# %%
# calculate LFP manually from imem_secs
laminar_lfp_man = list()
for ele_depth in depths:
ele_pos = (10, ele_depth, 10)
lfp = 0
for ii, (seg_im_list, sec) in enumerate(zip(imem_vecs.values(), allsecs)):
sec_start = np.array([sec.x3d(0), sec.y3d(0), sec.z3d(0)])
sec_end = np.array([sec.x3d(1), sec.y3d(1), sec.z3d(1)])
mid = (sec_start + sec_end) / 2
mult = calc_monopole_multiplier(ele_pos, mid, sigma=sigma)
for _h_seg_im in seg_im_list[1:-1]: # ignore endpoints (zero)
seg_im = np.array(_h_seg_im.to_python())
lfp += seg_im * mult
laminar_lfp_man.append(lfp)
# plot manual LFP
efig, eax = plt.subplots(1, 1)
for ii, lfp in enumerate(laminar_lfp_man):
eax.plot(times_lfp[tind] + ii * 2,
10 * ii + 10 * np.array(lfp)[:-1][tind],
color=cmap(ii / len(laminar_lfp_man)))
eax.text(200, 10 * ii, f'{int(depths[ii])}')
eax.set_ylim((-20, len(depths) * 10))
eax.set_yticks(())
eax.set_title('NMDA events at 250 ms (basal_1) and 400 ms (apical_2)')
eax.set_ylabel('Potential as function of distance from soma')
eax.set_xlabel('Time (ms)')
# %% Check that all currents are accounted for!
fig, ax = plt.subplots(1, 1, figsize=(6, 3))
# synapses at midpoints
n_seg_deep = int((l5p.sections['basal_1'].nseg + 2) / 2)
n_seg_superf = int((l5p.sections['apical_2'].nseg + 2) / 2)
syn_current = i_deep + i_superf
leak_currents = np.zeros(syn_current.shape)
for key, segs in imem_vecs.items():
for idx, seg_current in enumerate(segs):
this_seg_net_current = np.array(seg_current.to_python())
if key == 'basal_1' and idx == n_seg_deep:
this_seg_net_current -= i_deep
elif key == 'apical_2' and idx == n_seg_superf:
this_seg_net_current -= i_superf
leak_currents += np.array(this_seg_net_current)
ax.plot(times_lfp[tind], -syn_current[:-1][tind],
ls='--', lw=4, label='$-I_{syn}$')
ax.plot(times_lfp[tind], leak_currents[:-1][tind],
lw=2, label='$I_{leak}$')
ax.set_xlabel('Time (ms)')
ax.set_ylabel('Net current (nA)')
ax.legend()
fig.suptitle('Synaptic currents must leave cell as leak current')
plt.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment