copying to personal repo
This commit is contained in:
7
cnmodel/protocols/__init__.py
Normal file
7
cnmodel/protocols/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from .iv_curve import IVCurve
|
||||
from .vc_curve import VCCurve
|
||||
from .cc import CurrentClamp
|
||||
from .synapse_test import SynapseTest
|
||||
from .simple_synapse_test import SimpleSynapseTest
|
||||
from .protocol import Protocol
|
||||
from .population_test import PopulationTest
|
||||
91
cnmodel/protocols/cc.py
Normal file
91
cnmodel/protocols/cc.py
Normal file
@@ -0,0 +1,91 @@
|
||||
from neuron import h
|
||||
import numpy as np
|
||||
import scipy
|
||||
import scipy.integrate
|
||||
import scipy.stats
|
||||
|
||||
try:
|
||||
import pyqtgraph as pg
|
||||
|
||||
HAVE_PG = True
|
||||
except ImportError:
|
||||
HAVE_PG = False
|
||||
|
||||
from ..util.stim import make_pulse
|
||||
from .protocol import Protocol
|
||||
|
||||
|
||||
class CurrentClamp(Protocol):
|
||||
def __init__(self):
|
||||
super(CurrentClamp, self).__init__()
|
||||
|
||||
def run(self, cell, cmd, temp=22, dt=0.025):
|
||||
"""
|
||||
Run a single current-clamp recording on *section*.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cell : Cell
|
||||
The Cell instance to test. IClamp will be attached to
|
||||
cell.soma(0.5).
|
||||
cmd : array
|
||||
Array of current command values
|
||||
temp :
|
||||
temperature of simulation (22)
|
||||
dt :
|
||||
timestep of simulation (0.025)
|
||||
"""
|
||||
self.reset()
|
||||
self.cell = cell
|
||||
self.current_cmd = cmd
|
||||
self.dt = dt
|
||||
self.temp = temp
|
||||
|
||||
tend = len(cmd) * dt
|
||||
|
||||
# Configure IClamp
|
||||
istim = h.iStim(0.5, sec=cell.soma)
|
||||
istim.delay = 0
|
||||
istim.dur = 1e9 # these actually do not matter...
|
||||
istim.iMax = 0.0
|
||||
i_stim = h.Vector(cmd)
|
||||
i_stim.play(istim._ref_i, h.dt, 0)
|
||||
|
||||
# Connect recording vectors
|
||||
self["vm"] = cell.soma(0.5)._ref_v
|
||||
self["i_inj"] = istim._ref_i
|
||||
self["time"] = h._ref_t
|
||||
|
||||
# GO
|
||||
h.dt = dt
|
||||
h.celsius = temp
|
||||
h.tstop = tend
|
||||
cell.initialize()
|
||||
h.frecord_init()
|
||||
while h.t < h.tstop:
|
||||
h.fadvance()
|
||||
|
||||
def show(self):
|
||||
"""
|
||||
Plot results from run()
|
||||
"""
|
||||
if not HAVE_PG:
|
||||
raise Exception("Requires pyqtgraph")
|
||||
|
||||
#
|
||||
# Generate figure with subplots
|
||||
#
|
||||
app = pg.mkQApp()
|
||||
win = pg.GraphicsWindow()
|
||||
win.resize(1000, 800)
|
||||
Vplot = win.addPlot(labels={"left": "Vm (mV)", "bottom": "Time (ms)"})
|
||||
win.nextRow()
|
||||
Iplot = win.addPlot(labels={"left": "Iinj (nA)", "bottom": "Time (ms)"})
|
||||
|
||||
win.ci.layout.setRowStretchFactor(0, 10)
|
||||
win.ci.layout.setRowStretchFactor(1, 5)
|
||||
|
||||
Vplot.plot(self["time"], self["vm"])
|
||||
Iplot.plot(self["time"], self["i_inj"])
|
||||
|
||||
self.win = win
|
||||
185
cnmodel/protocols/democlamp.py
Normal file
185
cnmodel/protocols/democlamp.py
Normal file
@@ -0,0 +1,185 @@
|
||||
import os
|
||||
import os.path
|
||||
from neuron import h
|
||||
import pylibrary.Utility as U
|
||||
from ..util import PlotHelpers as PH
|
||||
import numpy as np
|
||||
import scipy
|
||||
import scipy.integrate
|
||||
import scipy.stats
|
||||
|
||||
try:
|
||||
import pyqtgraph as pg
|
||||
|
||||
HAVE_PG = True
|
||||
except ImportError:
|
||||
HAVE_PG = False
|
||||
|
||||
from ..util.stim import make_pulse
|
||||
|
||||
# import matplotlib as MP # must call first... before pylag/pyplot or backends
|
||||
# MP.use('Qt4Agg')
|
||||
|
||||
# import matplotlib.gridspec as GS
|
||||
# import mpl_toolkits.axes_grid1.inset_locator as INSETS
|
||||
# import mpl_toolkits.axes_grid1.anchored_artists as ANCHOR
|
||||
|
||||
# stdFont = 'Arial'
|
||||
# import matplotlib.pyplot as pylab
|
||||
# pylab.rcParams['interactive'] = False
|
||||
# pylab.rcParams['mathtext.default'] = 'sf'
|
||||
## next setting allows pdf font to be readable in Adobe Illustrator
|
||||
# pylab.rcParams['pdf.fonttype'] = 42
|
||||
# pylab.rcParams['figure.facecolor'] = 'white'
|
||||
|
||||
|
||||
def run_democlamp(cell, dend, vsteps=[-60, -70, -60], tsteps=[10, 50, 100]):
|
||||
"""
|
||||
Does some stuff.
|
||||
|
||||
"""
|
||||
f1 = pylab.figure(1)
|
||||
gs = GS.GridSpec(2, 2, width_ratios=[1, 1], height_ratios=[1, 1])
|
||||
|
||||
# note numbering for insets goes from 1 (upper right) to 4 (lower right)
|
||||
# counterclockwise
|
||||
pA = f1.add_subplot(gs[0])
|
||||
pAi = INSETS.inset_axes(pA, width="66%", height="40%", loc=2)
|
||||
pB = f1.add_subplot(gs[1])
|
||||
pBi = INSETS.inset_axes(pB, width="66%", height="40%", loc=4)
|
||||
pC = f1.add_subplot(gs[2])
|
||||
pCi = INSETS.inset_axes(pC, width="66%", height="40%", loc=2)
|
||||
pD = f1.add_subplot(gs[3])
|
||||
pDi = INSETS.inset_axes(pD, width="66%", height="40%", loc=1)
|
||||
# h.topology()
|
||||
|
||||
Ld = 0.5
|
||||
Ld2 = 1.0
|
||||
|
||||
VClamp = h.SEClamp(0.5, cell)
|
||||
VClamp.dur1 = tsteps[0]
|
||||
VClamp.amp1 = vsteps[0]
|
||||
VClamp.dur2 = tsteps[1]
|
||||
VClamp.amp2 = vsteps[1]
|
||||
VClamp.dur3 = tsteps[2]
|
||||
VClamp.amp3 = vsteps[2]
|
||||
Rs0 = 10.0
|
||||
VClamp.rs = Rs0
|
||||
compensation = [0.0, 70.0, 95.0]
|
||||
cms = [cell.cm * (100.0 - c) / 100.0 for c in compensation]
|
||||
|
||||
vrec = h.iStim(Ld, sec=dend[0])
|
||||
vrec.delay = 0
|
||||
vrec.dur = 1e9 # these actually do not matter...
|
||||
vrec.iMax = 0.0
|
||||
vrec2 = h.iStim(Ld2, sec=dend[0])
|
||||
vrec2.delay = 0
|
||||
vrec2.dur = 1e9 # these actually do not matter...
|
||||
vrec2.iMax = 0.0
|
||||
|
||||
stim = {}
|
||||
stim["NP"] = 1
|
||||
stim["Sfreq"] = 20 # stimulus frequency
|
||||
stim["delay"] = tsteps[0]
|
||||
stim["dur"] = tsteps[1]
|
||||
stim["amp"] = vsteps[1]
|
||||
stim["PT"] = 0.0
|
||||
stim["hold"] = vsteps[0]
|
||||
# (secmd, maxt, tstims) = make_pulse(stim)
|
||||
tend = 79.5
|
||||
linetype = ["-", "-", "-"]
|
||||
linethick = [0.5, 0.75, 1.25]
|
||||
linecolor = [[0.66, 0.66, 0.66], [0.4, 0.4, 0.3], "k"]
|
||||
n = 0
|
||||
vcmds = [-70, -20]
|
||||
vplots = [(pA, pAi, pC, pCi), (pB, pBi, pD, pDi)]
|
||||
for m, VX in enumerate(vcmds):
|
||||
stim["amp"] = VX
|
||||
pl = vplots[m]
|
||||
print(m, VX)
|
||||
(secmd, maxt, tstims) = make_pulse(stim)
|
||||
for n, rsc in enumerate(compensation):
|
||||
vec = {}
|
||||
for var in ["VCmd", "i_inj", "time", "Vsoma", "Vdend", "Vdend2", "VCmdR"]:
|
||||
vec[var] = h.Vector()
|
||||
VClamp.rs = Rs0 * (100.0 - rsc) / 100.0
|
||||
cell.cm = cms[n]
|
||||
# print VClamp.rs, cell.cm, VClamp.rs*cell.cm
|
||||
vec["VCmd"] = h.Vector(secmd)
|
||||
vec["Vsoma"].record(cell(0.5)._ref_v, sec=cell)
|
||||
vec["Vdend"].record(dend[0](Ld)._ref_v, sec=dend[0])
|
||||
vec["time"].record(h._ref_t)
|
||||
vec["i_inj"].record(VClamp._ref_i, sec=cell)
|
||||
|
||||
vec["VCmdR"].record(VClamp._ref_vc, sec=cell)
|
||||
VClamp.amp2 = VX
|
||||
# vec['VCmd'].play(VClamp.amp2, h.dt, 0, sec=cell)
|
||||
|
||||
h.tstop = tend
|
||||
h.init()
|
||||
h.finitialize(-60)
|
||||
h.run()
|
||||
vc = np.asarray(vec["Vsoma"])
|
||||
tc = np.asarray(vec["time"])
|
||||
|
||||
# now plot the data, raw and as insets
|
||||
for k in [0, 1]:
|
||||
pl[k].plot(
|
||||
vec["time"],
|
||||
vec["i_inj"],
|
||||
color=linecolor[n],
|
||||
linestyle=linetype[n],
|
||||
linewidth=linethick[n],
|
||||
)
|
||||
yl = pl[k].get_ylim()
|
||||
if k == 0:
|
||||
pass
|
||||
# pl[k].set_ylim([1.5*yl[0], -1.5*yl[1]])
|
||||
else:
|
||||
pass
|
||||
|
||||
for k in [2, 3]:
|
||||
pl[k].plot(
|
||||
vec["time"],
|
||||
vec["Vsoma"],
|
||||
color=linecolor[n],
|
||||
linestyle=linetype[n],
|
||||
linewidth=linethick[n],
|
||||
)
|
||||
pl[k].plot(
|
||||
vec["time"],
|
||||
vec["VCmdR"],
|
||||
color=linecolor[n],
|
||||
linestyle="--",
|
||||
linewidth=1,
|
||||
dashes=(1, 1),
|
||||
)
|
||||
pl[k].plot(
|
||||
vec["time"],
|
||||
vec["Vdend"],
|
||||
color=linecolor[n],
|
||||
linestyle=linetype[n],
|
||||
linewidth=linethick[n],
|
||||
dashes=(3, 3),
|
||||
)
|
||||
if VX < vsteps[0]:
|
||||
pl[k].set_ylim([-72, -40])
|
||||
else:
|
||||
pl[k].set_ylim([-62, VX + 30])
|
||||
|
||||
ptx = 10.8
|
||||
pBi.set_xlim([9.8, ptx])
|
||||
pAi.set_xlim([9.8, ptx])
|
||||
PH.setX(pAi, pCi)
|
||||
PH.setX(pBi, pDi)
|
||||
pD.set_ylim([-65, 10])
|
||||
# PH.setY(pC, pCi) # match Y limits
|
||||
PH.cleanAxes([pA, pAi, pB, pBi, pC, pCi, pD, pDi])
|
||||
PH.formatTicks([pA, pB, pC, pD], axis="x", fmt="%d")
|
||||
PH.formatTicks([pC, pD], axis="y", fmt="%d")
|
||||
PH.calbar(pAi, [ptx - 1, 0, 0.2, 2.0])
|
||||
PH.calbar(pCi, [ptx - 1, -50.0, 0.2, 10])
|
||||
PH.calbar(pBi, [ptx - 1, 0, 0.2, 10])
|
||||
PH.calbar(pDi, [ptx - 1, -50.0, 0.2, 20])
|
||||
pylab.draw()
|
||||
pylab.show()
|
||||
612
cnmodel/protocols/iv_curve.py
Normal file
612
cnmodel/protocols/iv_curve.py
Normal file
@@ -0,0 +1,612 @@
|
||||
from __future__ import print_function
|
||||
from neuron import h
|
||||
import numpy as np
|
||||
import scipy
|
||||
import scipy.integrate
|
||||
import scipy.stats
|
||||
import scipy.optimize
|
||||
|
||||
try:
|
||||
import pyqtgraph as pg
|
||||
|
||||
HAVE_PG = True
|
||||
except ImportError:
|
||||
HAVE_PG = False
|
||||
|
||||
from ..util.stim import make_pulse
|
||||
from ..util import fitting
|
||||
from ..util import custom_init
|
||||
from .protocol import Protocol
|
||||
|
||||
|
||||
class IVCurve(Protocol):
|
||||
def __init__(self):
|
||||
super(IVCurve, self).__init__()
|
||||
|
||||
def reset(self):
|
||||
super(IVCurve, self).reset()
|
||||
self.voltage_traces = []
|
||||
self.durs = None # durations of current steps
|
||||
self.current_cmd = None # Current command levels
|
||||
self.current_traces = []
|
||||
self.time_values = None
|
||||
self.dt = None
|
||||
self.initdelay = 0.0
|
||||
|
||||
def run(
|
||||
self,
|
||||
ivrange,
|
||||
cell,
|
||||
durs=None,
|
||||
sites=None,
|
||||
reppulse=None,
|
||||
temp=22,
|
||||
dt=0.025,
|
||||
initdelay=0.0,
|
||||
):
|
||||
"""
|
||||
Run a current-clamp I/V curve on *cell*.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ivrange : dict of list of tuples
|
||||
Each item in the list is (min, max, step) describing a range of
|
||||
levels to test. Range values are inclusive, so the max value may
|
||||
appear in the test values. Using multiple ranges allows finer
|
||||
measurements in some ranges.
|
||||
For example::
|
||||
|
||||
{'pulse': [(-1., 0., 1.), (-0.1, 0., 0.02)], 'prepulse': [(-0.5, 0, 0.1)]}
|
||||
|
||||
Optional keys include 'pulsedur' : the duration of the pulse, in ms
|
||||
'prepulsecur: the duration of the prepulse, in ms
|
||||
The prepulse or the pulse can have a single value if the other is ranged.
|
||||
cell : Cell
|
||||
The Cell instance to test.
|
||||
durs : tuple
|
||||
durations of (pre, pulse, post) regions of the command
|
||||
sites : list
|
||||
Sections to add recording electrodes
|
||||
reppulse :
|
||||
stimulate with pulse train
|
||||
temp :
|
||||
temperature of simulation (32)
|
||||
dt :
|
||||
timestep of simulation (0.025)
|
||||
|
||||
"""
|
||||
self.reset()
|
||||
self.cell = cell
|
||||
self.initdelay = initdelay
|
||||
self.dt = dt
|
||||
self.temp = temp
|
||||
# Calculate current pulse levels
|
||||
icur = []
|
||||
precur = [0.0]
|
||||
self.pre_current_cmd = []
|
||||
npresteps = 0
|
||||
if isinstance(ivrange["pulse"], tuple):
|
||||
icmd = [ivrange["pulse"]] # convert to a list with tuple(s) embedded
|
||||
else:
|
||||
icmd = ivrange["pulse"] # was already a list with multiple tuples
|
||||
for c in icmd: # unpack current levels for the main pulse
|
||||
try:
|
||||
(imin, imax, istep) = c # unpack a tuple... or list
|
||||
except:
|
||||
raise TypeError(
|
||||
"run_iv arguments must be a dict with tuples {'pulse': (imin, imax, istep), 'prepulse': ...}"
|
||||
)
|
||||
nstep = np.floor((imax - imin) / istep) + 1
|
||||
icur.extend(imin + istep * np.arange(nstep)) # build the list
|
||||
self.current_cmd = np.array(sorted(icur))
|
||||
nsteps = self.current_cmd.shape[0]
|
||||
# Configure IClamp
|
||||
if durs is None:
|
||||
durs = [10.0, 100.0, 50.0] # set default durs
|
||||
|
||||
if "prepulse" in ivrange.keys():
|
||||
if isinstance(ivrange["prepulse"], tuple):
|
||||
icmd = [ivrange["prepulse"]] # convert to a list with tuple(s) embedded
|
||||
else:
|
||||
icmd = ivrange["prepulse"] # was already a list with multiple tuples
|
||||
precur = []
|
||||
for c in icmd:
|
||||
try:
|
||||
(imin, imax, istep) = c # unpack a tuple... or list
|
||||
except:
|
||||
raise TypeError(
|
||||
"run_iv arguments must be a dict with tuples {'pulse': (imin, imax, istep), 'prepulse': ...}"
|
||||
)
|
||||
nstep = np.floor((imax - imin) / istep) + 1
|
||||
precur.extend(imin + istep * np.arange(nstep)) # build the list
|
||||
self.pre_current_cmd = np.array(sorted(precur))
|
||||
npresteps = self.pre_current_cmd.shape[0]
|
||||
durs.insert(1, 50.0)
|
||||
|
||||
self.durs = durs
|
||||
# set up stimulation with a pulse train
|
||||
if reppulse is not None:
|
||||
stim = {
|
||||
"NP": 10,
|
||||
"Sfreq": 50.0,
|
||||
"delay": 10.0,
|
||||
"dur": 2,
|
||||
"amp": 1.0,
|
||||
"PT": 0.0,
|
||||
"dt": self.dt,
|
||||
}
|
||||
elif "prepulse" in ivrange.keys():
|
||||
stim = {
|
||||
"NP": 2,
|
||||
"delay": durs[0],
|
||||
"predur": durs[1],
|
||||
"dur": durs[2],
|
||||
"amp": 1.0,
|
||||
"preamp": 0.0,
|
||||
"dt": self.dt,
|
||||
}
|
||||
self.p_start = durs[0] + durs[1]
|
||||
self.p_end = self.p_start + durs[2]
|
||||
self.p_dur = durs[2]
|
||||
else:
|
||||
stim = {
|
||||
"NP": 1,
|
||||
"delay": durs[0],
|
||||
"dur": durs[1],
|
||||
"amp": 1.0,
|
||||
"dt": self.dt,
|
||||
}
|
||||
self.p_start = durs[0]
|
||||
self.p_end = self.p_start + durs[1]
|
||||
self.p_dur = durs[1]
|
||||
# print stim
|
||||
# print('p_: ', self.p_start, self.p_end, self.p_dur)
|
||||
istim = h.iStim(0.5, sec=cell.soma)
|
||||
istim.delay = 5.0
|
||||
istim.dur = 1e9 # these actually do not matter...
|
||||
istim.iMax = 0.0
|
||||
self.tend = np.sum(durs) # maxt + len(iextend)*stim['dt']
|
||||
|
||||
self.cell = cell
|
||||
for i in range(nsteps):
|
||||
# Generate current command for this level
|
||||
stim["amp"] = self.current_cmd[i]
|
||||
if npresteps > 0:
|
||||
for j in range(npresteps):
|
||||
stim["preamp"] = self.pre_current_cmd[j]
|
||||
self.run_one(istim, stim, initflag=(i == 0 and j == 0))
|
||||
else:
|
||||
self.run_one(istim, stim, initflag=(i == 0))
|
||||
|
||||
def run_one(self, istim, stim, initflag=True):
|
||||
"""
|
||||
Perform one run in current-clamp for the selected cell
|
||||
and add the data to the traces
|
||||
|
||||
Parameters
|
||||
----------
|
||||
istim : Stimulus electrode instance
|
||||
stim : waveform information
|
||||
initflag : boolean (default: True)
|
||||
If true, force initialziation of the cell and computation of
|
||||
point Rin, tau and Vm
|
||||
"""
|
||||
(secmd, maxt, tstims) = make_pulse(stim)
|
||||
# print('maxt, dt*lencmd: ', maxt, len(secmd)*self.dt)# secmd = np.append(secmd, [0.])
|
||||
# print('stim: ', stim, self.tend)
|
||||
|
||||
# connect current command vector
|
||||
playvector = h.Vector(secmd)
|
||||
playvector.play(istim._ref_i, h.dt, 0, sec=self.cell.soma)
|
||||
|
||||
# Connect recording vectors
|
||||
self["v_soma"] = self.cell.soma(0.5)._ref_v
|
||||
# self['q10'] = self.cell.soma(0.5).ihpyr_adj._ref_q10
|
||||
# self['ih_ntau'] = self.cell.soma(0.5).ihpyr_adj._ref_kh_n_tau
|
||||
self["i_inj"] = istim._ref_i
|
||||
self["time"] = h._ref_t
|
||||
|
||||
# h('secondorder=0') # direct call fails; let hoc do the work
|
||||
h.celsius = self.cell.status["temperature"]
|
||||
self.cell.cell_initialize()
|
||||
h.dt = self.dt
|
||||
custom_init(v_init=self.cell.vm0)
|
||||
|
||||
h.t = 0.0
|
||||
h.tstop = self.tend
|
||||
while h.t < h.tstop:
|
||||
h.fadvance()
|
||||
|
||||
self.voltage_traces.append(self["v_soma"])
|
||||
self.current_traces.append(self["i_inj"])
|
||||
self.time_values = np.array(self["time"] - self.initdelay)
|
||||
# self.mon_q10 = np.array(self['q10'])
|
||||
# self.mon_ih_ntau = np.array(self['ih_ntau'])
|
||||
|
||||
def peak_vm(self, window=0.5):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
window : float (default: 0.5)
|
||||
fraction of trace to look at to find peak value
|
||||
|
||||
Returns
|
||||
-------
|
||||
peak membrane voltage for each trace.
|
||||
|
||||
"""
|
||||
Vm = self.voltage_traces
|
||||
Icmd = self.current_cmd
|
||||
steps = len(Icmd)
|
||||
peakStart = int(self.p_start / self.dt)
|
||||
peakStop = int(
|
||||
peakStart + (self.p_dur * window) / self.dt
|
||||
) # peak can be in first half
|
||||
Vpeak = []
|
||||
for i in range(steps):
|
||||
if Icmd[i] > 0:
|
||||
Vpeak.append(Vm[i][peakStart:peakStop].max())
|
||||
else:
|
||||
Vpeak.append(Vm[i][peakStart:peakStop].min())
|
||||
return np.array(Vpeak)
|
||||
|
||||
def steady_vm(self, window=0.1):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
window: (float) default: 0.1
|
||||
fraction of window to use for steady-state measurement, taken
|
||||
immediately before the end of the step
|
||||
|
||||
Returns
|
||||
-------
|
||||
steady-state membrane voltage for each trace.
|
||||
|
||||
"""
|
||||
Vm = self.voltage_traces
|
||||
steps = len(Vm)
|
||||
steadyStop = int((self.p_end) / self.dt)
|
||||
steadyStart = int(
|
||||
steadyStop - (self.p_end * window) / self.dt
|
||||
) # measure last 10% of trace
|
||||
Vsteady = [Vm[i][steadyStart:steadyStop].mean() for i in range(steps)]
|
||||
return np.array(Vsteady)
|
||||
|
||||
def spike_times(self, threshold=None):
|
||||
"""
|
||||
Return an array of spike times for each trace.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
threshold: float (default: None)
|
||||
Optional threshold at which to detect spikes. By
|
||||
default, this queries cell.spike_threshold.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of spike times.
|
||||
|
||||
"""
|
||||
if threshold is None:
|
||||
threshold = self.cell.spike_threshold
|
||||
|
||||
Vm = self.voltage_traces
|
||||
steps = len(Vm)
|
||||
spikes = []
|
||||
for i in range(steps):
|
||||
# dvdt = np.diff(Vm[i]) / self.dt
|
||||
# mask = (dvdt > 40).astype(int)
|
||||
mask = (Vm[i] > threshold).astype(int)
|
||||
indexes = np.argwhere(np.diff(mask) == 1)[:, 0] + 1
|
||||
times = indexes.astype(float) * self.dt
|
||||
spikes.append(times)
|
||||
return spikes
|
||||
|
||||
def spike_filter(self, spikes, window=(0.0, np.inf)):
|
||||
"""Filter the spikes to only those occurring in a defined window.
|
||||
|
||||
Required to compute input resistance in traces with no spikes during
|
||||
the stimulus, because some traces will have anodal break spikes.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
spikes : list
|
||||
the list of spike trains returned from the spike_times method
|
||||
window : (start, stop)
|
||||
the window over which to look for spikes (in msec: default is
|
||||
the entire trace).
|
||||
|
||||
Returns
|
||||
-------
|
||||
the spikes in a list
|
||||
|
||||
"""
|
||||
filteredspikes = []
|
||||
for i in range(len(spikes)):
|
||||
winspikes = [] # spikes is arranged by current; so this is for one level
|
||||
for j in range(len(spikes[i])):
|
||||
if spikes[i][j] >= window[0] and spikes[i][j] <= window[1]:
|
||||
winspikes.append(spikes[i][j])
|
||||
filteredspikes.append(winspikes) # now build filtered spike list
|
||||
return filteredspikes
|
||||
|
||||
def rest_vm(self):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
None
|
||||
|
||||
Returns
|
||||
-------
|
||||
The mean resting membrane potential.
|
||||
"""
|
||||
d = int(self.durs[0] / self.dt)
|
||||
rvm = np.array(
|
||||
[
|
||||
np.array(self.voltage_traces[i][d // 2 : d]).mean()
|
||||
for i in range(len(self.voltage_traces))
|
||||
]
|
||||
).mean()
|
||||
return rvm
|
||||
|
||||
def input_resistance_tau(self, vmin=-10.0, imax=0, return_fits=False):
|
||||
"""
|
||||
Estimate resting input resistance and time constant.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
vmin : float
|
||||
minimum voltage to use in computation relative to resting
|
||||
imax : float
|
||||
maximum current to use in computation.
|
||||
return_eval : bool
|
||||
If True, return lmfit.ModelFit instances for the subthreshold trace
|
||||
fits as well.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict :
|
||||
Dict containing:
|
||||
|
||||
* 'slope' and 'intercept' keys giving linear
|
||||
regression for subthreshold traces near rest
|
||||
* 'tau' giving the average first-exponential fit time constant
|
||||
* 'fits' giving a record array of exponential fit data to subthreshold
|
||||
traces.
|
||||
|
||||
Analyzes only traces hyperpolarizing pulse traces near rest, with no
|
||||
spikes.
|
||||
|
||||
"""
|
||||
Vss = self.steady_vm()
|
||||
vmin += self.rest_vm()
|
||||
Icmd = self.current_cmd
|
||||
rawspikes = self.spike_times()
|
||||
spikes = self.spike_filter(rawspikes, window=[self.p_start, self.p_end])
|
||||
steps = len(Icmd)
|
||||
|
||||
nSpikes = np.array([len(s) for s in spikes])
|
||||
# find traces with Icmd < 0, Vm > -70, and no spikes.
|
||||
vmask = Vss >= vmin
|
||||
imask = Icmd <= imax
|
||||
smask = nSpikes > 0
|
||||
mask = vmask & imask & ~smask
|
||||
if mask.sum() < 2:
|
||||
print(
|
||||
"WARNING: Not enough traces to do linear regression in "
|
||||
"IVCurve.input_resistance_tau()."
|
||||
)
|
||||
print(
|
||||
"{0:<15s}: {1:s}".format(
|
||||
"vss", ", ".join(["{:.2f}".format(v) for v in Vss])
|
||||
)
|
||||
)
|
||||
print(
|
||||
"{0:<15s}: {1:s}".format(
|
||||
"Icmd", ", ".join(["{:.2f}".format(i) for i in Icmd])
|
||||
)
|
||||
)
|
||||
print("{0:<15s}: {1:s}".format("vmask", repr(vmask.astype(int))))
|
||||
print("{0:<15s}: {1:s} ".format("imask", repr(imask.astype(int))))
|
||||
print("{0:<15s}: {1:s}".format("spikemask", repr(smask.astype(int))))
|
||||
raise Exception(
|
||||
"Not enough traces to do linear regression (see info above)."
|
||||
)
|
||||
|
||||
# Use these to measure input resistance by linear regression.
|
||||
reg = scipy.stats.linregress(Icmd[mask], Vss[mask])
|
||||
(slope, intercept, r, p, stderr) = reg
|
||||
|
||||
# also measure the tau in the same traces:
|
||||
pulse_start = int(self.p_start / self.dt)
|
||||
pulse_stop = int((self.p_end) / self.dt)
|
||||
|
||||
fits = []
|
||||
fit_inds = []
|
||||
tx = self.time_values[pulse_start:pulse_stop].copy()
|
||||
for i, m in enumerate(mask):
|
||||
if not m or (self.rest_vm() - Vss[i]) <= 1:
|
||||
continue
|
||||
trace = self.voltage_traces[i][pulse_start:pulse_stop]
|
||||
|
||||
# find first minimum in the trace
|
||||
min_ind = np.argmin(trace)
|
||||
min_val = trace[min_ind]
|
||||
min_diff = trace[0] - min_val
|
||||
tau_est = min_ind * self.dt * (1.0 - 1.0 / np.e)
|
||||
# print ('minind: ', min_ind, tau_est)
|
||||
fit = fitting.Exp1().fit(
|
||||
trace[:min_ind],
|
||||
method="nelder",
|
||||
x=tx[:min_ind],
|
||||
xoffset=(tx[0], "fixed"),
|
||||
yoffset=(min_val, -120.0, -10.0),
|
||||
amp=(min_diff, 0.0, 50.0),
|
||||
tau=(tau_est, 0.5, 50.0),
|
||||
)
|
||||
|
||||
# find first maximum in the trace (following with first minimum)
|
||||
max_ind = np.argmax(trace[min_ind:]) + min_ind
|
||||
max_val = trace[max_ind]
|
||||
max_diff = min_val - max_val
|
||||
tau2_est = max_ind * self.dt * (1.0 - 1.0 / np.e)
|
||||
amp1_est = fit.params["amp"].value
|
||||
tau1_est = fit.params["tau"].value
|
||||
amp2_est = fit.params["yoffset"] - max_val
|
||||
# print('tau1, tau2est: ', tau1_est, tau2_est)
|
||||
# fit up to first maximum with double exponential, using prior
|
||||
# fit as seed.
|
||||
fit = fitting.Exp2().fit(
|
||||
trace[:max_ind],
|
||||
method="nelder",
|
||||
x=tx[:max_ind],
|
||||
xoffset=(tx[0], "fixed"),
|
||||
yoffset=(max_val, -120.0, -10.0),
|
||||
amp1=(amp1_est, 0.0, 200.0),
|
||||
tau1=(tau1_est, 0.5, 50.0),
|
||||
amp2=(amp2_est, -200.0, -0.5),
|
||||
tau_ratio=(tau2_est / tau1_est, 2.0, 50.0),
|
||||
tau2="tau_ratio * tau1",
|
||||
)
|
||||
|
||||
fits.append(fit)
|
||||
fit_inds.append(i)
|
||||
# convert fits to record array
|
||||
# print len(fits) # fits[0].params
|
||||
if len(fits) > 0:
|
||||
key_order = sorted(
|
||||
fits[0].params
|
||||
) # to ensure that unit tests remain stable
|
||||
dtype = [(k, float) for k in key_order] + [("index", int)]
|
||||
fit_data = np.empty(len(fits), dtype=dtype)
|
||||
for i, fit in enumerate(fits):
|
||||
for k, v in fit.params.items():
|
||||
fit_data[i][k] = v.value
|
||||
fit_data[i]["index"] = fit_inds[i]
|
||||
|
||||
if "tau" in fit_data.dtype.fields:
|
||||
tau = fit_data["tau"].mean()
|
||||
else:
|
||||
tau = fit_data["tau1"].mean()
|
||||
else:
|
||||
slope = 0.0
|
||||
intercept = 0.0
|
||||
tau = 0.0
|
||||
fit_data = []
|
||||
ret = {"slope": slope, "intercept": intercept, "tau": tau, "fits": fit_data}
|
||||
|
||||
if return_fits:
|
||||
return ret, fits
|
||||
else:
|
||||
return ret
|
||||
|
||||
def show(self, cell=None, rmponly=False):
|
||||
"""
|
||||
Plot results from run_iv()
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cell : cell object (default: None)
|
||||
|
||||
"""
|
||||
if not HAVE_PG:
|
||||
raise Exception("Requires pyqtgraph")
|
||||
|
||||
#
|
||||
# Generate figure with subplots
|
||||
#
|
||||
app = pg.mkQApp()
|
||||
win = pg.GraphicsWindow(
|
||||
"%s %s (%s)"
|
||||
% (cell.status["name"], cell.status["modelType"], cell.status["species"])
|
||||
)
|
||||
self.win = win
|
||||
win.resize(1000, 800)
|
||||
Vplot = win.addPlot(labels={"left": "Vm (mV)", "bottom": "Time (ms)"})
|
||||
rightGrid = win.addLayout(rowspan=2)
|
||||
win.nextRow()
|
||||
Iplot = win.addPlot(labels={"left": "Iinj (nA)", "bottom": "Time (ms)"})
|
||||
|
||||
IVplot = rightGrid.addPlot(labels={"left": "Vm (mV)", "bottom": "Icmd (nA)"})
|
||||
IVplot.showGrid(x=True, y=True)
|
||||
rightGrid.nextRow()
|
||||
spikePlot = rightGrid.addPlot(
|
||||
labels={"left": "Iinj (nA)", "bottom": "Spike times (ms)"}
|
||||
)
|
||||
rightGrid.nextRow()
|
||||
FIplot = rightGrid.addPlot(
|
||||
labels={"left": "Spike count", "bottom": "Iinj (nA)"}
|
||||
)
|
||||
|
||||
win.ci.layout.setRowStretchFactor(0, 10)
|
||||
win.ci.layout.setRowStretchFactor(1, 5)
|
||||
|
||||
#
|
||||
# Plot simulation and analysis results
|
||||
#
|
||||
Vm = self.voltage_traces
|
||||
Iinj = self.current_traces
|
||||
Icmd = self.current_cmd
|
||||
t = self.time_values
|
||||
steps = len(Icmd)
|
||||
|
||||
# plot I, V traces
|
||||
colors = [(i, steps * 3.0 / 2.0) for i in range(steps)]
|
||||
for i in range(steps):
|
||||
Vplot.plot(t, Vm[i], pen=colors[i])
|
||||
Iplot.plot(t, Iinj[i], pen=colors[i])
|
||||
|
||||
if rmponly:
|
||||
return
|
||||
# I/V relationships
|
||||
IVplot.plot(
|
||||
Icmd,
|
||||
self.peak_vm(),
|
||||
symbol="o",
|
||||
symbolBrush=(50, 150, 50, 255),
|
||||
symbolSize=4.0,
|
||||
)
|
||||
IVplot.plot(Icmd, self.steady_vm(), symbol="s", symbolSize=4.0)
|
||||
|
||||
# F/I relationship and raster plot
|
||||
spikes = self.spike_times()
|
||||
for i, times in enumerate(spikes):
|
||||
spikePlot.plot(
|
||||
x=times,
|
||||
y=[Icmd[i]] * len(times),
|
||||
pen=None,
|
||||
symbol="d",
|
||||
symbolBrush=colors[i],
|
||||
symbolSize=4.0,
|
||||
)
|
||||
FIplot.plot(x=Icmd, y=[len(s) for s in spikes], symbol="o", symbolSize=4.0)
|
||||
|
||||
# Print Rm, Vrest
|
||||
rmtau, fits = self.input_resistance_tau(return_fits=True)
|
||||
s = rmtau["slope"]
|
||||
i = rmtau["intercept"]
|
||||
# tau1 = rmtau['fits']['tau1'].mean()
|
||||
# tau2 = rmtau['fits']['tau2'].mean()
|
||||
# print ("\nMembrane resistance (chord): {0:0.1f} MOhm Taum1: {1:0.2f} Taum2: {2:0.2f}".format(s, tau1, tau2))
|
||||
|
||||
# Plot linear subthreshold I/V relationship
|
||||
ivals = np.array([Icmd.min(), Icmd.max()])
|
||||
vvals = s * ivals + i
|
||||
line = pg.QtGui.QGraphicsLineItem(ivals[0], vvals[0], ivals[1], vvals[1])
|
||||
line.setPen(pg.mkPen(255, 0, 0, 70))
|
||||
line.setZValue(-10)
|
||||
IVplot.addItem(line, ignoreBounds=True)
|
||||
|
||||
# plot exponential fits
|
||||
for fit in fits:
|
||||
t = np.linspace(self.p_start, self.p_end, 1000)
|
||||
y = fit.eval(x=t)
|
||||
Vplot.plot(
|
||||
t, y, pen={"color": (100, 100, 0), "style": pg.QtCore.Qt.DashLine}
|
||||
)
|
||||
|
||||
# plot initial guess
|
||||
# y = fit.eval(x=t, **fit.init_params.valuesdict())
|
||||
# Vplot.plot(t, y, pen={'color': 'b', 'style': pg.QtCore.Qt.DashLine})
|
||||
|
||||
print("Resting membrane potential: %0.1f mV\n" % self.rest_vm())
|
||||
150
cnmodel/protocols/population_test.py
Normal file
150
cnmodel/protocols/population_test.py
Normal file
@@ -0,0 +1,150 @@
|
||||
from __future__ import print_function
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
|
||||
from neuron import h
|
||||
|
||||
import cnmodel.util as util
|
||||
from .protocol import Protocol
|
||||
from ..util import custom_init
|
||||
from cnmodel.util import sound
|
||||
|
||||
|
||||
class PopulationTest(Protocol):
|
||||
def reset(self):
|
||||
super(PopulationTest, self).reset()
|
||||
|
||||
def run(
|
||||
self, pops, cf=16e3, temp=34.0, dt=0.025, stim="sound", simulator="cochlea"
|
||||
):
|
||||
"""
|
||||
1. Connect pop1 => pop2
|
||||
2. Instantiate a single cell in pop2
|
||||
3. Automatically generate presynaptic cells and synapses from pop1
|
||||
4. Stimulate presynaptic cells and record postsynaptically
|
||||
"""
|
||||
|
||||
pre_pop, post_pop = pops
|
||||
pre_pop.connect(post_pop)
|
||||
self.pre_pop = pre_pop
|
||||
self.post_pop = post_pop
|
||||
|
||||
# start with one cell, selected from the user-selected population, that has
|
||||
# a cf close to the center CF
|
||||
post_cell_ind = post_pop.select(1, cf=cf, create=True)[0]
|
||||
post_cell = post_pop.get_cell(post_cell_ind)
|
||||
post_pop.resolve_inputs(depth=1)
|
||||
post_sec = post_cell.soma
|
||||
self.post_cell_ind = post_cell_ind
|
||||
self.post_cell = post_cell
|
||||
|
||||
pre_cell_inds = post_pop.cell_connections(post_cell_ind)[pre_pop]
|
||||
pre_cells = [pre_pop.get_cell(i) for i in pre_cell_inds]
|
||||
pre_secs = [cell.soma for cell in pre_cells]
|
||||
self.pre_cells = pre_cells
|
||||
self.pre_cell_inds = pre_cell_inds
|
||||
self.stim = sound.TonePip(
|
||||
rate=100e3,
|
||||
duration=0.1,
|
||||
f0=cf,
|
||||
dbspl=60,
|
||||
ramp_duration=2.5e-3,
|
||||
pip_duration=0.05,
|
||||
pip_start=[0.02],
|
||||
)
|
||||
|
||||
##
|
||||
## voltage clamp the target cell
|
||||
##
|
||||
# clampV = 40.0
|
||||
# vccontrol = h.VClamp(0.5, sec=post_cell.soma)
|
||||
# vccontrol.dur[0] = 10.0
|
||||
# vccontrol.amp[0] = clampV
|
||||
# vccontrol.dur[1] = 100.0
|
||||
# vccontrol.amp[1] = clampV
|
||||
# vccontrol.dur[2] = 20.0
|
||||
# vccontrol.amp[2] = clampV
|
||||
|
||||
#
|
||||
# set up stimulation of the presynaptic cells
|
||||
#
|
||||
self.stim_params = []
|
||||
self.istim = []
|
||||
for i, pre_cell in enumerate(pre_cells):
|
||||
if stim == "sound":
|
||||
pre_cell.set_sound_stim(self.stim, seed=i, simulator=simulator)
|
||||
amp = 0.0
|
||||
else:
|
||||
amp = 3.0
|
||||
istim = h.iStim(0.5, sec=pre_cell.soma)
|
||||
stim = {}
|
||||
stim["NP"] = 10
|
||||
stim["Sfreq"] = 100.0 # stimulus frequency
|
||||
stim["delay"] = 10.0
|
||||
stim["dur"] = 0.5
|
||||
stim["amp"] = amp
|
||||
stim["PT"] = 0.0
|
||||
stim["dt"] = dt
|
||||
(secmd, maxt, tstims) = util.make_pulse(stim)
|
||||
self.stim_params.append(stim)
|
||||
|
||||
# istim current pulse train
|
||||
i_stim_vec = h.Vector(secmd)
|
||||
i_stim_vec.play(istim._ref_i, dt, 0, pre_cell.soma(0.5))
|
||||
self.istim.append((istim, i_stim_vec))
|
||||
self["istim"] = istim._ref_i
|
||||
|
||||
# record presynaptic Vm
|
||||
self["v_pre%d" % i] = pre_cell.soma(0.5)._ref_v
|
||||
|
||||
self["t"] = h._ref_t
|
||||
self["v_post"] = post_cell.soma(0.5)._ref_v
|
||||
|
||||
#
|
||||
# Run simulation
|
||||
#
|
||||
h.dt = dt
|
||||
self.dt = dt
|
||||
h.celsius = post_cell.status["temperature"]
|
||||
self.temp = h.celsius
|
||||
post_cell.cell_initialize() # proper initialization.
|
||||
h.dt = self.dt
|
||||
custom_init(v_init=post_cell.vm0)
|
||||
h.t = 0.0
|
||||
h.tstop = 200.0
|
||||
while h.t < h.tstop:
|
||||
h.fadvance()
|
||||
|
||||
def show(self):
|
||||
print(
|
||||
"Connected %d presynaptic cells to 1 postsynaptic cell."
|
||||
% len(self.pre_cell_inds)
|
||||
)
|
||||
print("Postsynaptic CF = %0.2f" % self.post_pop.cells[self.post_cell_ind]["cf"])
|
||||
print("Presynaptic CF = %s" % self.pre_pop.cells[self.pre_cell_inds]["cf"])
|
||||
|
||||
self.win = pg.GraphicsWindow()
|
||||
self.win.resize(1000, 1000)
|
||||
|
||||
cmd_plot = self.win.addPlot(title="Stim")
|
||||
try:
|
||||
cmd_plot.plot(self["t"], self["istim"])
|
||||
except:
|
||||
pass
|
||||
|
||||
self.win.nextRow()
|
||||
pre_plot = self.win.addPlot(title=self.pre_cells[0].type + " Vm")
|
||||
for i in range(len(self.pre_cells)):
|
||||
pre_plot.plot(
|
||||
self["t"],
|
||||
self["v_pre%d" % i],
|
||||
pen=pg.mkPen(
|
||||
pg.intColor(i, len(self.pre_cells)),
|
||||
hues=len(self.pre_cells),
|
||||
width=1.0,
|
||||
),
|
||||
)
|
||||
|
||||
self.win.nextRow()
|
||||
post_plot = self.win.addPlot(title="Post Cell: %s" % self.post_cell.type)
|
||||
post_plot.plot(self["t"], self["v_post"])
|
||||
43
cnmodel/protocols/protocol.py
Normal file
43
cnmodel/protocols/protocol.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from neuron import h
|
||||
import numpy as np
|
||||
from ..util import random_seed, custom_init
|
||||
|
||||
|
||||
class Protocol(object):
|
||||
"""
|
||||
Base class providing common tools for running, analyzing, and displaying
|
||||
simulations.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
self._vectors = {}
|
||||
|
||||
def run(self, seed=None):
|
||||
"""
|
||||
Run this protocol.
|
||||
|
||||
Subclasses should extend this method.
|
||||
"""
|
||||
if seed is not None:
|
||||
random_seed.set_seed(seed)
|
||||
self.reset()
|
||||
|
||||
def __setitem__(self, name, variable):
|
||||
"""
|
||||
Record *variable* during the next run.
|
||||
"""
|
||||
vec = h.Vector()
|
||||
self._vectors[name] = vec
|
||||
vec.record(variable)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""
|
||||
Return a np array for previously recorded data given *name*.
|
||||
"""
|
||||
return np.array(self._vectors[name])
|
||||
|
||||
def custom_init(self, vinit=-60.0):
|
||||
return custom_init(vinit)
|
||||
111
cnmodel/protocols/simple_synapse_test.py
Normal file
111
cnmodel/protocols/simple_synapse_test.py
Normal file
@@ -0,0 +1,111 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
from neuron import h
|
||||
import pyqtgraph as pg
|
||||
|
||||
import cnmodel.util as util
|
||||
from .protocol import Protocol
|
||||
from .. import cells
|
||||
|
||||
|
||||
class SimpleSynapseTest(Protocol):
|
||||
def reset(self):
|
||||
super(SimpleSynapseTest, self).reset()
|
||||
|
||||
def run(
|
||||
self,
|
||||
pre_sec,
|
||||
post_sec,
|
||||
temp=34.0,
|
||||
dt=0.025,
|
||||
vclamp=-65.0,
|
||||
iterations=1,
|
||||
tstop=200.0,
|
||||
stim_params=None,
|
||||
**kwds
|
||||
):
|
||||
"""
|
||||
"""
|
||||
Protocol.run(self, **kwds)
|
||||
|
||||
pre_cell = cells.cell_from_section(pre_sec)
|
||||
post_cell = cells.cell_from_section(post_sec)
|
||||
synapse = pre_cell.connect(post_cell, type="simple")
|
||||
|
||||
self.synapse = synapse
|
||||
self.pre_sec = synapse.terminal.section
|
||||
self.post_sec = synapse.psd.section
|
||||
self.pre_cell = pre_cell
|
||||
self.post_cell = post_cell
|
||||
|
||||
#
|
||||
# voltage clamp the target cell
|
||||
#
|
||||
vccontrol = h.VClamp(0.5, sec=post_cell.soma)
|
||||
vccontrol.dur[0] = tstop
|
||||
vccontrol.amp[0] = vclamp
|
||||
# vccontrol.dur[1] = 100.0
|
||||
# vccontrol.amp[1] = clampV
|
||||
# vccontrol.dur[2] = 20.0
|
||||
# vccontrol.amp[2] = clampV
|
||||
|
||||
#
|
||||
# set up stimulation of the presynaptic axon/terminal
|
||||
#
|
||||
|
||||
istim = h.iStim(0.5, sec=pre_cell.soma)
|
||||
stim = {
|
||||
"NP": 10,
|
||||
"Sfreq": 100.0,
|
||||
"delay": 10.0,
|
||||
"dur": 0.5,
|
||||
"amp": 10.0,
|
||||
"PT": 0.0,
|
||||
"dt": dt,
|
||||
}
|
||||
if stim_params is not None:
|
||||
stim.update(stim_params)
|
||||
(secmd, maxt, tstims) = util.make_pulse(stim)
|
||||
self.stim = stim
|
||||
|
||||
if tstop is None:
|
||||
tstop = len(secmd) * dt
|
||||
|
||||
istim.delay = 0
|
||||
istim.dur = 1e9 # these actually do not matter...
|
||||
istim.iMax = 0.0
|
||||
|
||||
# istim current pulse train
|
||||
i_stim_vec = h.Vector(secmd)
|
||||
i_stim_vec.play(istim._ref_i, dt, 0)
|
||||
|
||||
#
|
||||
# Run simulation
|
||||
#
|
||||
h.tstop = tstop # duration of a run
|
||||
h.celsius = temp
|
||||
h.dt = dt
|
||||
self.temp = temp
|
||||
self.dt = dt
|
||||
for nrep in list(range(iterations)): # could do multiple runs....
|
||||
self.reset()
|
||||
self["v_pre"] = pre_cell.soma(0.5)._ref_v
|
||||
self["t"] = h._ref_t
|
||||
self["v_soma"] = post_cell.soma(0.5)._ref_v
|
||||
self["i_soma"] = vccontrol._ref_i
|
||||
util.custom_init()
|
||||
h.run()
|
||||
|
||||
def show(self):
|
||||
self.win = pg.GraphicsWindow()
|
||||
self.win.resize(800, 800)
|
||||
t = self["t"]
|
||||
|
||||
p1 = self.win.addPlot(title=self.pre_cell.status["name"])
|
||||
p1.setLabels(left="V pre (mV)", bottom="Time (ms)")
|
||||
p1.plot(t, self["v_pre"])
|
||||
|
||||
self.win.nextRow()
|
||||
p2 = self.win.addPlot(title=self.post_cell.status["name"])
|
||||
p2.plot(t[1:], self["i_soma"][1:], pen=pg.mkPen("w", width=2))
|
||||
p2.setLabels(left="I post (nA)", bottom="Time (ms)")
|
||||
p2.setXLink(p1)
|
||||
779
cnmodel/protocols/synapse_test.py
Normal file
779
cnmodel/protocols/synapse_test.py
Normal file
@@ -0,0 +1,779 @@
|
||||
from __future__ import print_function
|
||||
from collections import OrderedDict
|
||||
from scipy import interpolate
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
from neuron import h
|
||||
import cnmodel.util as util
|
||||
from .protocol import Protocol
|
||||
from .. import cells
|
||||
from ..synapses import GluPSD, GlyPSD, Exp2PSD
|
||||
from ..util.find_point import find_crossing
|
||||
import timeit
|
||||
|
||||
|
||||
class SynapseTest(Protocol):
|
||||
def reset(self):
|
||||
super(SynapseTest, self).reset()
|
||||
|
||||
def run(
|
||||
self,
|
||||
pre_sec,
|
||||
post_sec,
|
||||
n_synapses,
|
||||
temp=34.0,
|
||||
dt=0.025,
|
||||
vclamp=40.0,
|
||||
iterations=1,
|
||||
tstop=240.0,
|
||||
stim_params=None,
|
||||
synapsetype="multisite",
|
||||
**kwds
|
||||
):
|
||||
"""
|
||||
Basic synapse test. Connects sections of two cells with *n_synapses*.
|
||||
The cells are allowed to negotiate the details of the connecting
|
||||
synapse. The presynaptic soma is then driven with a pulse train
|
||||
followed by a recovery pulse of varying delay.
|
||||
|
||||
*stim_params* is an optional dictionary with keys 'NP', 'Sfreq', 'delay',
|
||||
'dur', 'amp'.
|
||||
|
||||
Analyses:
|
||||
|
||||
* Distribution of PSG amplitude, kinetics, and latency
|
||||
* Synaptic depression / facilitation and recovery timecourses
|
||||
"""
|
||||
Protocol.run(self, **kwds)
|
||||
|
||||
pre_cell = cells.cell_from_section(pre_sec)
|
||||
post_cell = cells.cell_from_section(post_sec)
|
||||
synapses = []
|
||||
for i in range(n_synapses):
|
||||
synapses.append(pre_cell.connect(post_cell, type=synapsetype))
|
||||
if len(synapses) == 0:
|
||||
raise ValueError("No synapses created for this cell combination!")
|
||||
self.synapses = synapses
|
||||
self.pre_sec = synapses[0].terminal.section
|
||||
self.post_sec = synapses[0].psd.section
|
||||
self.pre_cell = pre_cell
|
||||
self.post_cell = post_cell
|
||||
self.plots = {} # store plot information here
|
||||
#
|
||||
# voltage clamp the target cell
|
||||
#
|
||||
clampV = vclamp
|
||||
vccontrol = h.VClamp(0.5, sec=post_cell.soma)
|
||||
vccontrol.dur[0] = 10.0
|
||||
vccontrol.amp[0] = clampV
|
||||
vccontrol.dur[1] = 100.0
|
||||
vccontrol.amp[1] = clampV
|
||||
vccontrol.dur[2] = 20.0
|
||||
vccontrol.amp[2] = clampV
|
||||
|
||||
#
|
||||
# set up stimulation of the presynaptic axon/terminal
|
||||
#
|
||||
|
||||
istim = h.iStim(0.5, sec=pre_cell.soma)
|
||||
stim = {
|
||||
"NP": 10,
|
||||
"Sfreq": 100.0,
|
||||
"delay": 10.0,
|
||||
"dur": 0.5,
|
||||
"amp": 10.0,
|
||||
"PT": 0.0,
|
||||
"dt": dt,
|
||||
}
|
||||
if stim_params is not None:
|
||||
stim.update(stim_params)
|
||||
(secmd, maxt, tstims) = util.make_pulse(stim)
|
||||
self.stim = stim
|
||||
|
||||
if tstop is None:
|
||||
tstop = len(secmd) * dt
|
||||
|
||||
istim.delay = 0
|
||||
istim.dur = 1e9 # these actually do not matter...
|
||||
istim.iMax = 0.0
|
||||
|
||||
# istim current pulse train
|
||||
i_stim_vec = h.Vector(secmd)
|
||||
i_stim_vec.play(istim._ref_i, dt, 0)
|
||||
|
||||
# create hoc vectors for each parameter we wish to monitor and display
|
||||
synapse = synapses[0]
|
||||
self.all_psd = []
|
||||
if isinstance(synapses[0].psd, GlyPSD) or isinstance(synapses[0].psd, GluPSD):
|
||||
for syn in synapses:
|
||||
self.all_psd.extend(syn.psd.all_psd)
|
||||
elif isinstance(synapses[0].psd, Exp2PSD):
|
||||
for syn in synapses:
|
||||
self.all_psd.append(syn)
|
||||
|
||||
# for i, cleft in enumerate(synapse.psd.clefts):
|
||||
# self['cleft_xmtr%d' % i] = cleft._ref_CXmtr
|
||||
# self['cleft_pre%d' % i] = cleft._ref_pre
|
||||
# self['cleft_xv%d' % i] = cleft._ref_XV
|
||||
# self['cleft_xc%d' % i] = cleft._ref_XC
|
||||
# self['cleft_xu%d' % i] = cleft._ref_XU
|
||||
|
||||
#
|
||||
# Run simulation
|
||||
#
|
||||
h.tstop = tstop # duration of a run
|
||||
h.celsius = temp
|
||||
h.dt = dt
|
||||
self.temp = temp
|
||||
self.dt = dt
|
||||
self.isoma = []
|
||||
self.currents = {"ampa": [], "nmda": []}
|
||||
self.all_releases = []
|
||||
self.all_release_events = []
|
||||
start_time = timeit.default_timer()
|
||||
for nrep in list(range(iterations)): # could do multiple runs....
|
||||
self.reset()
|
||||
self["v_pre"] = pre_cell.soma(0.5)._ref_v
|
||||
self["t"] = h._ref_t
|
||||
self["v_soma"] = pre_cell.soma(0.5)._ref_v
|
||||
if not isinstance(synapse.psd, Exp2PSD):
|
||||
self["relsite_xmtr"] = synapse.terminal.relsite._ref_XMTR[0]
|
||||
|
||||
if isinstance(synapse.psd, GluPSD):
|
||||
# make a synapse monitor for each release zone
|
||||
self.all_nmda = []
|
||||
self.all_ampa = []
|
||||
for syn in synapses:
|
||||
# collect all PSDs across all synapses
|
||||
self.all_ampa.extend(syn.psd.ampa_psd)
|
||||
self.all_nmda.extend(syn.psd.nmda_psd)
|
||||
|
||||
# Record current through all PSDs individually
|
||||
syn.psd.record("i", "g", "Open")
|
||||
|
||||
# for k,p in enumerate(self.all_nmda):
|
||||
# self['iNMDA%03d' % k] = p._ref_i
|
||||
# self['opNMDA%03d' % k] = p._ref_Open
|
||||
# for k,p in enumerate(self.all_ampa):
|
||||
# self['iAMPA%03d' % k] = p._ref_i
|
||||
# self['opAMPA%03d' % k] = p._ref_Open
|
||||
|
||||
elif isinstance(synapse.psd, GlyPSD):
|
||||
# Record current through all PSDs individually
|
||||
for k, p in enumerate(self.all_psd):
|
||||
self["iGLY%03d" % k] = p._ref_i
|
||||
self["opGLY%03d" % k] = p._ref_Open
|
||||
|
||||
psd = self.all_psd
|
||||
if synapse.psd.psdType == "glyslow":
|
||||
nstate = 7
|
||||
self["C0"] = psd[0]._ref_C0
|
||||
self["C1"] = psd[0]._ref_C1
|
||||
self["C2"] = psd[0]._ref_C2
|
||||
self["O1"] = psd[0]._ref_O1
|
||||
self["O2"] = psd[0]._ref_O2
|
||||
self["D1"] = psd[0]._ref_D1
|
||||
# self['D3'] = psd[0]._ref_D3
|
||||
# self['O1'] = psd[0]._ref_O1
|
||||
elif synapse.psd.psdType == "glyfast":
|
||||
nstate = 7
|
||||
self["C0"] = psd[0]._ref_C0
|
||||
self["C1"] = psd[0]._ref_C1
|
||||
self["C2"] = psd[0]._ref_C2
|
||||
self["C3"] = psd[0]._ref_C3
|
||||
self["O1"] = psd[0]._ref_O1
|
||||
self["O2"] = psd[0]._ref_O2
|
||||
elif isinstance(synapse.psd, Exp2PSD):
|
||||
self["iPSD"] = self.all_psd[0].psd.syn._ref_i
|
||||
|
||||
if not isinstance(synapse.psd, Exp2PSD):
|
||||
for i, s in enumerate(synapses):
|
||||
s.terminal.relsite.rseed = util.random_seed.current_seed() + nrep
|
||||
util.custom_init()
|
||||
h.run()
|
||||
|
||||
# add up psd current across all runs
|
||||
if isinstance(synapse.psd, GluPSD):
|
||||
iampa = np.zeros_like(synapse.psd.get_vector("ampa", "i"))
|
||||
inmda = iampa.copy()
|
||||
for syn in self.synapses:
|
||||
for i in range(syn.psd.n_psd):
|
||||
iampa += syn.psd.get_vector("ampa", "i", i)
|
||||
inmda += syn.psd.get_vector("nmda", "i", i)
|
||||
isoma = iampa + inmda
|
||||
self.currents["ampa"].append(iampa)
|
||||
self.currents["nmda"].append(inmda)
|
||||
elif isinstance(synapse.psd, GlyPSD):
|
||||
isoma = np.zeros_like(self["iGLY000"])
|
||||
for k in range(len(self.all_psd)):
|
||||
isoma += self["iGLY%03d" % k]
|
||||
elif isinstance(synapse.psd, Exp2PSD):
|
||||
isoma = self["iPSD"]
|
||||
self.isoma.append(isoma)
|
||||
self.all_releases.append(self.release_timings())
|
||||
self.all_release_events.append(self.release_events())
|
||||
|
||||
elapsed = timeit.default_timer() - start_time
|
||||
print("Elapsed time for %d Repetions: %f" % (iterations, elapsed))
|
||||
|
||||
def release_events(self, syn_no=0):
|
||||
"""
|
||||
Analyze results and return a dict of values related to terminal release
|
||||
probability:
|
||||
|
||||
n_zones: Array containing the number of release zones for each
|
||||
synapse.
|
||||
|
||||
n_requests: Array containing number of release requests for each
|
||||
synapse. Note for multi-zone synapses, a single
|
||||
presynaptic spike results in one release request _per_
|
||||
zone.
|
||||
|
||||
n_releases: Array containing actual number of releases for each
|
||||
synapse.
|
||||
|
||||
tot_requests: The total number of release requests across all
|
||||
release zones.
|
||||
|
||||
tot_releases: The total number of releases.
|
||||
|
||||
release_p: Release probability computed as
|
||||
tot_releases / tot_requests
|
||||
|
||||
|
||||
"""
|
||||
synapse = self.synapses[syn_no]
|
||||
|
||||
ret = {
|
||||
"n_zones": [0],
|
||||
"n_spikes": [0],
|
||||
"n_requests": [0],
|
||||
"n_releases": [0],
|
||||
"tot_requests": 0,
|
||||
"tot_releases": 0,
|
||||
"release_p": 0.0,
|
||||
}
|
||||
#
|
||||
# Count spikes and releases for each terminal
|
||||
#
|
||||
if not isinstance(self.synapses[0].psd, Exp2PSD):
|
||||
ret["n_zones"] = np.array([syn.terminal.n_rzones for syn in self.synapses])
|
||||
ret["n_spikes"] = np.array(
|
||||
[syn.terminal.relsite.nRequests for syn in self.synapses]
|
||||
)
|
||||
ret["n_requests"] = ret["n_spikes"] * ret["n_zones"]
|
||||
ret["n_releases"] = np.array(
|
||||
[syn.terminal.relsite.nReleases for syn in self.synapses]
|
||||
)
|
||||
|
||||
#
|
||||
# Compute release probability
|
||||
#
|
||||
# total number of release requests
|
||||
ret["tot_requests"] = ret["n_requests"].sum()
|
||||
# total number of actual release events
|
||||
ret["tot_releases"] = ret["n_releases"].sum()
|
||||
|
||||
if ret["tot_requests"] > 0:
|
||||
ret["release_p"] = float(ret["tot_releases"]) / ret["tot_requests"]
|
||||
else:
|
||||
ret["release_p"] = np.nan
|
||||
|
||||
return ret
|
||||
|
||||
def release_timings(self):
|
||||
"""
|
||||
Return a list of arrays (one array per synapse) describing the timing
|
||||
and latency of release events.
|
||||
"""
|
||||
data = []
|
||||
if isinstance(self.synapses[0].psd, Exp2PSD):
|
||||
return data
|
||||
|
||||
for j in range(0, len(self.synapses)):
|
||||
relsite = self.synapses[j].terminal.relsite
|
||||
nev = int(relsite.ev_index)
|
||||
ev = np.empty(nev, dtype=[("time", float), ("latency", float)])
|
||||
ev["latency"] = np.array(relsite.EventLatencies)[:nev]
|
||||
ev["time"] = np.array(relsite.EventTime)[:nev]
|
||||
data.append(ev)
|
||||
return data
|
||||
|
||||
def open_probability(self):
|
||||
"""
|
||||
Analyze results and return a dict of values related to psd open
|
||||
probability:
|
||||
|
||||
nmda: (imax, opmax)
|
||||
ampa: (imax, opmax)
|
||||
gly: (imax, opmax)
|
||||
"""
|
||||
synapse = self.synapses[0]
|
||||
if isinstance(synapse.psd, GluPSD) and len(synapse.psd.nmda_psd) > 0:
|
||||
# find a psd with ampa and nmda currents
|
||||
nmImax = []
|
||||
amImax = []
|
||||
nmOmax = []
|
||||
amOmax = []
|
||||
# self.win.nextRow()
|
||||
for syn in self.synapses:
|
||||
for i in range(syn.psd.n_psd):
|
||||
nm = np.abs(syn.psd.get_vector("nmda", "i", i)).max()
|
||||
am = np.abs(syn.psd.get_vector("ampa", "i", i)).max()
|
||||
opnm = np.abs(syn.psd.get_vector("nmda", "Open", i)).max()
|
||||
opam = np.abs(syn.psd.get_vector("ampa", "Open", i)).max()
|
||||
if nm > 1e-6 or am > 1e-6: # only count releases, not failures
|
||||
nmImax.append(nm)
|
||||
amImax.append(am)
|
||||
nmOmax.append(opnm)
|
||||
amOmax.append(opam)
|
||||
break
|
||||
if nmImax != 0:
|
||||
break
|
||||
|
||||
return {
|
||||
"nmda": OrderedDict(
|
||||
[
|
||||
("Imax", np.mean(nmImax)),
|
||||
("Omax", np.mean(nmOmax)),
|
||||
# ('OmaxMax', np.max(nmOmax)), # was used for testing...
|
||||
# ('OmaxMin', np.min(nmOmax))
|
||||
]
|
||||
),
|
||||
"ampa": OrderedDict(
|
||||
[
|
||||
("Imax", np.mean(amImax)),
|
||||
("Omax", np.mean(amOmax)),
|
||||
# ('OmaxMax', np.max(amOmax)),
|
||||
# ('OmaxMin', np.min(amOmax))
|
||||
]
|
||||
),
|
||||
}
|
||||
|
||||
elif isinstance(synapse.psd, GlyPSD) and len(synapse.psd.all_psd) > 0:
|
||||
# find a psd with ampa and nmda currents
|
||||
glyImax = 0
|
||||
glyOmax = 0
|
||||
for i in range(len(self.all_psd)):
|
||||
imax = np.abs(self["iGLY%03d" % i]).max()
|
||||
omax = np.abs(self["opGLY%03d" % i]).max()
|
||||
|
||||
return {"gly": (glyImax, glyOmax)}
|
||||
|
||||
elif isinstance(synapse.psd, Exp2PSD):
|
||||
return {"Exp2PSD": (0.0, 0.0)}
|
||||
|
||||
def analyze_events(self):
|
||||
events = []
|
||||
for run in range(len(self.isoma)):
|
||||
events.append(self.analyze_events_in_run(runno=run))
|
||||
return events
|
||||
|
||||
def analyze_events_in_run(self, runno=0):
|
||||
"""
|
||||
Analyze postsynaptic events for peak, latency, and shape.
|
||||
|
||||
Todo:
|
||||
- This currently analyzes cumulative currents; might be better to
|
||||
analyze individual PSD currents
|
||||
- Measure decay time constant, rate of facilitation/depression,
|
||||
recovery.
|
||||
|
||||
"""
|
||||
stim = self.stim
|
||||
ipi = 1000.0 / stim["Sfreq"] # convert from Hz (seconds) to msec.
|
||||
t_extend = 0.25 # allow response detection into the next frame
|
||||
extend_pts = int(t_extend / self.dt)
|
||||
|
||||
pscpts = (
|
||||
int(ipi / self.dt) + extend_pts
|
||||
) # number of samples to analyze for each psc
|
||||
ipsc = np.zeros((stim["NP"], pscpts)) # storage for psc currents
|
||||
tpsc = np.arange(
|
||||
0, ipi + t_extend, self.dt
|
||||
) # time values corresponding to *ipsc*
|
||||
|
||||
# mpl.figure(num=220, facecolor='w')
|
||||
# gpsc = mpl.subplot2grid((5, 2), (0, 0), rowspan=2, colspan=2)
|
||||
psc_20_lat = np.zeros((stim["NP"], 1)) # latency to 20% of rising amplitude
|
||||
psc_80_lat = np.zeros((stim["NP"], 1)) # latency to 80% of rising amplitude
|
||||
psc_hw = np.zeros((stim["NP"], 1)) # width at half-height
|
||||
psc_rt = np.zeros((stim["NP"], 1)) # 20-80 rise time
|
||||
tp = np.zeros((stim["NP"], 1)) # pulse time relative to first pulse
|
||||
events = np.zeros(
|
||||
stim["NP"],
|
||||
dtype=[
|
||||
("20% latency", float),
|
||||
("80% latency", float),
|
||||
("half width", float),
|
||||
("half left", float),
|
||||
("half right", float),
|
||||
("rise time", float),
|
||||
("pulse time", float),
|
||||
("peak", float),
|
||||
("peak index", int),
|
||||
],
|
||||
)
|
||||
events[:] = np.nan
|
||||
|
||||
minLat = 0.0 # minimum latency for an event, in ms
|
||||
minStart = int(
|
||||
minLat / self.dt
|
||||
) # first index relative to pulse to search for psc peak
|
||||
|
||||
for i in range(stim["NP"]):
|
||||
tstart = stim["delay"] + i * ipi # pulse start time
|
||||
events["pulse time"][i] = tstart
|
||||
istart = int(tstart / self.dt) # pulse start index
|
||||
tp[i] = tstart - stim["delay"]
|
||||
iend = istart + pscpts
|
||||
# print 'istart: %d iend: %d, len(isoma): %d\n' % (istart, iend, len(self.isoma[runno]))
|
||||
ipsc[i, :] = np.abs(self.isoma[runno][istart:iend])
|
||||
psc_pk = minStart + np.argmax(
|
||||
ipsc[i, minStart : -(extend_pts + 1)]
|
||||
) # position of the peak
|
||||
|
||||
# print 'i, pscpk, ipsc[i,pscpk]: ', i, psc_pk, ipsc[i, psc_pk]
|
||||
# print 'minLat: %f ipi+t_extend: %f, hdt: %f' % ((minLat, ipi+t_extend, self.dt))
|
||||
if psc_pk == minStart:
|
||||
continue
|
||||
pkval = ipsc[i, psc_pk]
|
||||
events["peak"][i] = pkval
|
||||
events["peak index"][i] = psc_pk
|
||||
|
||||
# Find 20% and 80% crossing points to the left of the PSC peak
|
||||
pscmin = ipsc[i, :psc_pk].min()
|
||||
|
||||
lat20 = (
|
||||
find_crossing(
|
||||
ipsc[i],
|
||||
start=psc_pk,
|
||||
direction=-1,
|
||||
threshold=(pscmin + (pkval - pscmin) * 0.2),
|
||||
)
|
||||
* self.dt
|
||||
)
|
||||
lat80 = (
|
||||
find_crossing(
|
||||
ipsc[i],
|
||||
start=psc_pk,
|
||||
direction=-1,
|
||||
threshold=(pscmin + (pkval - pscmin) * 0.8),
|
||||
)
|
||||
* self.dt
|
||||
)
|
||||
events["20% latency"][i] = lat20
|
||||
events["80% latency"][i] = lat80
|
||||
|
||||
# Find 50% crossing points on either side of the PSC peak
|
||||
psc_50l = (
|
||||
find_crossing(
|
||||
ipsc[i],
|
||||
start=psc_pk,
|
||||
direction=-1,
|
||||
threshold=(pscmin + (pkval - pscmin) * 0.5),
|
||||
)
|
||||
* self.dt
|
||||
)
|
||||
psc_50r = (
|
||||
find_crossing(
|
||||
ipsc[i],
|
||||
start=psc_pk,
|
||||
direction=1,
|
||||
threshold=(pscmin + (pkval - pscmin) * 0.5),
|
||||
)
|
||||
* self.dt
|
||||
)
|
||||
|
||||
events["half left"] = psc_50l
|
||||
events["half right"] = psc_50r
|
||||
|
||||
if not np.isnan(lat20) and not np.isnan(lat80):
|
||||
events["rise time"][i] = lat80 - lat20
|
||||
else:
|
||||
events["rise time"][i] = np.nan
|
||||
if not np.isnan(psc_50r) and not np.isnan(psc_50l):
|
||||
events["half width"][i] = float(psc_50r) - float(psc_50l)
|
||||
# gpsc.plot(psc_50l, pkval * 0.5, 'k+')
|
||||
# gpsc.plot(psc_50r, pkval * 0.5, 'k+')
|
||||
# gpsc.plot(tpsc, ipsc[i, :].T)
|
||||
else:
|
||||
events["half width"][i] = np.nan
|
||||
|
||||
return events
|
||||
|
||||
def hide(self):
|
||||
if hasattr(self, "win"):
|
||||
self.win.hide()
|
||||
|
||||
def show_result(
|
||||
self, releasePlot=True, probabilityPlot=True, glyPlot=False, plotFocus="EPSC"
|
||||
):
|
||||
synapse = self.synapses[0]
|
||||
|
||||
#
|
||||
# Print parameters related to release probability
|
||||
#
|
||||
events = self.release_events()
|
||||
ns = len(self.synapses)
|
||||
for i in range(ns):
|
||||
if i < len(events["n_spikes"]) and events["n_spikes"][i] > 0:
|
||||
v = (
|
||||
i,
|
||||
events["n_spikes"][i],
|
||||
events["n_zones"][i],
|
||||
events["n_releases"][i],
|
||||
)
|
||||
print("Synapse %d: spikes: %d zones: %d releases: %d" % v)
|
||||
print("")
|
||||
print("Total release requests: %d" % events["tot_requests"])
|
||||
print("Total release events: %d" % events["tot_releases"])
|
||||
print("Release probability: %8.3f" % events["release_p"])
|
||||
if not isinstance(synapse.psd, Exp2PSD):
|
||||
prel_final = synapse.terminal.relsite.Dn * synapse.terminal.relsite.Fn
|
||||
print("Final release probability (Dn * Fn): %8.3f" % prel_final)
|
||||
|
||||
#
|
||||
# Compute NMDA / AMPA open probability
|
||||
#
|
||||
print("")
|
||||
oprob = self.open_probability()
|
||||
if "gly" in oprob:
|
||||
glyImax, glyOPmax = oprob["gly"]
|
||||
print("Max GLYR Open Prob: %f" % (glyOPmax,))
|
||||
print("Max GLYR I: %f" % (glyImax,))
|
||||
elif "ampa" in oprob or "nmda" in oprob:
|
||||
nmImax, nmOPmax = oprob["nmda"].values()
|
||||
amImax, amOPmax = oprob["ampa"].values()
|
||||
print("Max NMDAR Open Prob: %f AMPA Open Prob: %f" % (nmOPmax, amOPmax))
|
||||
print("Max NMDAR I: %f AMPA I: %f" % (nmImax, amImax))
|
||||
if nmImax + amImax != 0.0:
|
||||
print(" N/(N+A): %f\n" % (nmImax / (nmImax + amImax)))
|
||||
else:
|
||||
print(" (no NMDA/AMPA current; release might have failed)")
|
||||
|
||||
self.win = pg.GraphicsWindow()
|
||||
self.win.resize(1000, 1000)
|
||||
self.win.show()
|
||||
|
||||
#
|
||||
# Plot pre/postsynaptic currents
|
||||
#
|
||||
t = self["t"]
|
||||
|
||||
p1 = self.win.addPlot(title=self.pre_cell.status["name"])
|
||||
p1.setLabels(left="V pre (mV)", bottom="Time (ms)")
|
||||
p1.plot(t, self["v_pre"])
|
||||
self.plots["VPre"] = p1
|
||||
|
||||
if plotFocus == "EPSC":
|
||||
self.win.nextRow()
|
||||
p2 = self.win.addPlot(title=self.post_cell.status["name"])
|
||||
for i, isoma in enumerate(self.isoma):
|
||||
p2.plot(t, isoma, pen=(i, len(self.isoma)))
|
||||
p2.plot(t, np.mean(self.isoma, axis=0), pen=pg.mkPen("w", width=2))
|
||||
p2.setLabels(left="Total PSD current (nA)", bottom="Time (ms)")
|
||||
p2.setXLink(p1)
|
||||
self.plots["EPSC"] = p2
|
||||
else:
|
||||
# todo: resurrect this?
|
||||
g2 = mpl.subplot2grid((6, 1), (1, 0), rowspan=1)
|
||||
g2.plot(t, self.isoma, color="cyan")
|
||||
g3 = mpl.subplot2grid((6, 1), (2, 0))
|
||||
g3.plot(t, self["v_pre"], color="blue")
|
||||
g3.plot(t, self["v_soma"], color="red")
|
||||
g4 = mpl.subplot2grid((6, 1), (3, 0))
|
||||
p4 = g4.plot(t, self["relsite_xmtr"]) # glutamate
|
||||
g4.axes.set_ylabel("relsite_xmtr")
|
||||
g5 = mpl.subplot2grid((6, 1), (4, 0))
|
||||
for k, p in enumerate(synapse.psd.all_psd):
|
||||
if p.hname().find("NMDA", 0, 6) >= 0:
|
||||
g5.plot(t, self["isyn%03d" % k]) # current through nmdar
|
||||
g5.axes.set_ylabel("inmda")
|
||||
g6 = mpl.subplot2grid((6, 1), (5, 0))
|
||||
for k, p in enumerate(synapse.psd.all_psd):
|
||||
if p.hname().find("NMDA", 0, 6) < 0:
|
||||
g6.plot(t, self["isyn%03d" % k]) # glutamate
|
||||
g6.axes.set_ylabel("iAMPA")
|
||||
|
||||
#
|
||||
# Analyze the individual events.
|
||||
# EPSCs get rise time, latency, half-width, and decay tau estimates.
|
||||
#
|
||||
events = self.analyze_events()
|
||||
|
||||
eventno = 0
|
||||
self.win.nextRow()
|
||||
p3 = self.win.addPlot(
|
||||
labels={"left": "20%-80% Latency (ms)", "bottom": "Pulse Time (ms)"}
|
||||
)
|
||||
p3.plot(
|
||||
events[eventno]["pulse time"],
|
||||
events[eventno]["20% latency"],
|
||||
pen=None,
|
||||
symbol="o",
|
||||
)
|
||||
p3.plot(
|
||||
events[eventno]["pulse time"],
|
||||
events[eventno]["80% latency"],
|
||||
pen=None,
|
||||
symbol="t",
|
||||
)
|
||||
p3.setXLink(p1)
|
||||
self.plots["latency2080"] = p3
|
||||
|
||||
self.win.nextRow()
|
||||
p4 = self.win.addPlot(
|
||||
labels={"left": "Half Width (ms)", "bottom": "Pulse Time (ms)"}
|
||||
)
|
||||
p4.plot(
|
||||
events[eventno]["pulse time"],
|
||||
events[eventno]["half width"],
|
||||
pen=None,
|
||||
symbol="o",
|
||||
)
|
||||
p4.setXLink(p1)
|
||||
self.plots["halfwidth"] = p4
|
||||
|
||||
self.win.nextRow()
|
||||
p5 = self.win.addPlot(
|
||||
labels={"left": "Rise Time (ms)", "bottom": "Pulse Time (ms)"}
|
||||
)
|
||||
p5.plot(
|
||||
events[eventno]["pulse time"],
|
||||
events[eventno]["rise time"],
|
||||
pen=None,
|
||||
symbol="o",
|
||||
)
|
||||
p5.setXLink(p1)
|
||||
self.plots["RT"] = p5
|
||||
|
||||
#
|
||||
# Print average values from events
|
||||
#
|
||||
nst = range(self.stim["NP"])
|
||||
analysisWindow = [nst[0:2], nst[-5:]]
|
||||
|
||||
RT_mean2080_early = np.nanmean(events[eventno]["rise time"][analysisWindow[0]])
|
||||
RT_mean2080_late = np.nanmean(events[eventno]["rise time"][analysisWindow[1]])
|
||||
Lat_mean20_early = np.nanmean(events[eventno]["20% latency"][analysisWindow[0]])
|
||||
Lat_mean20_late = np.nanmean(events[eventno]["20% latency"][analysisWindow[1]])
|
||||
HW_mean_early = np.nanmean(events[eventno]["half width"][analysisWindow[0]])
|
||||
HW_mean_late = np.nanmean(events[eventno]["half width"][analysisWindow[1]])
|
||||
print("\n--------------")
|
||||
print("Means:")
|
||||
print("--------------")
|
||||
# print RT_mean2080_early
|
||||
# print Lat_mean20_early
|
||||
# print HW_mean_early
|
||||
print(
|
||||
"Early: RT {0:7.3f} ms Lat {1:7.3f} ms HW {2:7.3f} ms".format(
|
||||
RT_mean2080_early, Lat_mean20_early, HW_mean_early
|
||||
)
|
||||
)
|
||||
print(
|
||||
"Late : RT {0:7.3f} ms Lat {1:7.3f} ms HW {2:7.3f} ms".format(
|
||||
RT_mean2080_late, Lat_mean20_late, HW_mean_late
|
||||
)
|
||||
)
|
||||
RT_std2080_early = np.nanstd(events[eventno]["rise time"][analysisWindow[0]])
|
||||
RT_std2080_late = np.nanstd(events[eventno]["rise time"][analysisWindow[1]])
|
||||
Lat_std20_early = np.nanstd(events[eventno]["20% latency"][analysisWindow[0]])
|
||||
Lat_std20_late = np.nanstd(events[eventno]["20% latency"][analysisWindow[1]])
|
||||
HW_std_early = np.nanstd(events[eventno]["half width"][analysisWindow[0]])
|
||||
HW_std_late = np.nanstd(events[eventno]["half width"][analysisWindow[1]])
|
||||
print("\n--------------")
|
||||
print("Standard Deviations:")
|
||||
print("--------------")
|
||||
print(
|
||||
"Early: RT {0:7.3f} ms Lat {1:7.3f} ms HW {2:7.3f} ms".format(
|
||||
RT_std2080_early, Lat_std20_early, HW_std_early
|
||||
)
|
||||
)
|
||||
print(
|
||||
"Late : RT {0:7.3f} ms Lat {1:7.3f} ms HW {2:7.3f} ms".format(
|
||||
RT_std2080_late, Lat_std20_late, HW_std_late
|
||||
)
|
||||
)
|
||||
print("-----------------")
|
||||
|
||||
#
|
||||
# Plot release event distributions over time
|
||||
#
|
||||
if releasePlot:
|
||||
self.win.nextRow()
|
||||
p6 = self.win.addPlot(
|
||||
labels={"left": "Release latency", "bottom": "Time (ms)"}
|
||||
)
|
||||
p6.setXLink(p1)
|
||||
self.plots["latency"] = p6
|
||||
p7 = self.win.addPlot(
|
||||
labels={"left": "Release latency", "bottom": "Num. Releases"}
|
||||
)
|
||||
p7.setYLink(p6)
|
||||
self.plots["latency_distribution"] = p7
|
||||
self.win.ci.layout.setColumnFixedWidth(1, 200)
|
||||
if not isinstance(self.synapses[0].psd, Exp2PSD):
|
||||
|
||||
rel_events = self.all_releases
|
||||
all_latencies = []
|
||||
for i, trial in enumerate(rel_events):
|
||||
for syn in trial:
|
||||
p6.plot(
|
||||
syn["time"],
|
||||
syn["latency"],
|
||||
pen=None,
|
||||
symbolBrush=(i, len(rel_events)),
|
||||
symbolPen=(i, len(rel_events)),
|
||||
symbolSize=4,
|
||||
symbol="o",
|
||||
)
|
||||
all_latencies.append(syn["latency"])
|
||||
all_latencies = np.concatenate(all_latencies)
|
||||
(hist, binedges) = np.histogram(all_latencies)
|
||||
curve = p7.plot(
|
||||
binedges,
|
||||
hist,
|
||||
stepMode=True,
|
||||
fillBrush=(100, 100, 255, 150),
|
||||
fillLevel=0,
|
||||
)
|
||||
curve.rotate(-90)
|
||||
curve.scale(-1, 1)
|
||||
|
||||
# if probabilityPlot:
|
||||
# self.win.nextRow()
|
||||
# p8 = self.win.addPlot(labels={'left': 'Release Prob', 'bottom': 'Time (ms)'})
|
||||
# p8.setXLink(p1)
|
||||
# times = self.release_timings()
|
||||
# for isyn, syn in enumerate(self.synapses):
|
||||
# syn_events = self.release_events(syn_no=isyn)
|
||||
# Pr = syn_events['n_releases']/syn_events['n_requests'] # Pr for each stimulus
|
||||
# # print Pr
|
||||
#
|
||||
# i = 0 # ultimately would like to plot this for each synapse
|
||||
# p8.plot(events[0]['pulse time'], Pr, pen=None, symbolBrush=(i, len(self.all_releases)),
|
||||
# symbolPen=(i, len(events)), symbolSize=4, symbol='o')
|
||||
|
||||
#
|
||||
# Plot GlyR state variables
|
||||
#
|
||||
# if glyPlot:
|
||||
|
||||
# i = 0
|
||||
# if synapse.psd.psdType == 'glyslow':
|
||||
# mpl.figure(2)
|
||||
# for var in ['C0', 'C1', 'C2', 'O1', 'O1', 'D1', 'Open']:
|
||||
# mpl.subplot(nstate, 1, i + 1)
|
||||
# mpl.plot(t, self[var])
|
||||
# mpl.ylabel(var)
|
||||
# i = i + 1
|
||||
# if synapse.psd.psdType == 'glyfast':
|
||||
# mpl.figure(2)
|
||||
# for var in ['C0', 'C1', 'C2', 'C3', 'O1', 'O2', 'Open']:
|
||||
# mpl.subplot(7, 1, i + 1)
|
||||
# mpl.plot(t, self[var])
|
||||
# mpl.ylabel(var)
|
||||
# i = i + 1
|
||||
# mpl.draw()
|
||||
# mpl.show()
|
||||
206
cnmodel/protocols/vc_curve.py
Normal file
206
cnmodel/protocols/vc_curve.py
Normal file
@@ -0,0 +1,206 @@
|
||||
import os
|
||||
import os.path
|
||||
from neuron import h
|
||||
import numpy as np
|
||||
import scipy
|
||||
import scipy.integrate
|
||||
import scipy.stats
|
||||
|
||||
from .protocol import Protocol
|
||||
|
||||
|
||||
try:
|
||||
import pyqtgraph as pg
|
||||
|
||||
HAVE_PG = True
|
||||
except ImportError:
|
||||
HAVE_PG = False
|
||||
from ..util import custom_init
|
||||
from ..util.stim import make_pulse
|
||||
|
||||
# import matplotlib as MP # must call first... before pylag/pyplot or backends
|
||||
# MP.use('Qt4Agg')
|
||||
|
||||
# import matplotlib.gridspec as GS
|
||||
# import mpl_toolkits.axes_grid1.inset_locator as INSETS
|
||||
# import mpl_toolkits.axes_grid1.anchored_artists as ANCHOR
|
||||
|
||||
# stdFont = 'Arial'
|
||||
# import matplotlib.pyplot as pylab
|
||||
# pylab.rcParams['interactive'] = False
|
||||
# pylab.rcParams['mathtext.default'] = 'sf'
|
||||
## next setting allows pdf font to be readable in Adobe Illustrator
|
||||
# pylab.rcParams['pdf.fonttype'] = 42
|
||||
# pylab.rcParams['figure.facecolor'] = 'white'
|
||||
|
||||
|
||||
class VCCurve(Protocol):
|
||||
def __init__(self):
|
||||
super(VCCurve, self).__init__()
|
||||
|
||||
def reset(self):
|
||||
super(VCCurve, self).reset()
|
||||
self.voltage_traces = []
|
||||
self.current_traces = []
|
||||
self.durs = None # durations of current steps
|
||||
self.voltage_cmd = None # Current command levels
|
||||
self.time_values = None
|
||||
self.dt = None
|
||||
|
||||
def run(self, vcrange, cell, dt=0.025):
|
||||
"""
|
||||
Run voltage-clamp I/V curve.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
vmin : float
|
||||
Minimum voltage step value
|
||||
vmax :
|
||||
Maximum voltage step value
|
||||
vstep :
|
||||
Voltage difference between steps
|
||||
cell :
|
||||
The Cell instance to test.
|
||||
"""
|
||||
self.reset()
|
||||
self.cell = cell
|
||||
try:
|
||||
(vmin, vmax, vstep) = vcrange # unpack the tuple...
|
||||
except:
|
||||
raise TypeError("run_iv argument 1 must be a tuple (imin, imax, istep)")
|
||||
|
||||
vstim = h.SEClamp(0.5, cell.soma) # set up a single-electrode clamp
|
||||
vstim.dur1 = 50.0
|
||||
vstim.amp1 = -60
|
||||
vstim.dur2 = 500.0
|
||||
vstim.amp2 = -60.0
|
||||
vstim.dur3 = 400
|
||||
vstim.amp3 = -60.0
|
||||
vstim.rs = 0.01
|
||||
cell.soma.cm = 0.001 # reduce capacitative transients (cap compensation)
|
||||
self.durs = [vstim.dur1, vstim.dur2, vstim.dur3]
|
||||
self.amps = [vstim.amp1, vstim.amp2, vstim.amp3]
|
||||
self.voltage_cmd = []
|
||||
tend = 900.0
|
||||
iv_nstepv = int(np.ceil((vmax - vmin) / vstep))
|
||||
iv_minv = vmin
|
||||
iv_maxv = vmax
|
||||
vstep = (iv_maxv - iv_minv) / iv_nstepv
|
||||
for i in range(iv_nstepv):
|
||||
self.voltage_cmd.append(float(i * vstep) + iv_minv)
|
||||
nreps = iv_nstepv
|
||||
h.dt = dt
|
||||
self.dt = h.dt
|
||||
for i in range(nreps):
|
||||
# Connect recording vectors
|
||||
self["v_soma"] = cell.soma(0.5)._ref_v
|
||||
self["i_inj"] = vstim._ref_i
|
||||
self["time"] = h._ref_t
|
||||
vstim.amp2 = self.voltage_cmd[i]
|
||||
custom_init(v_init=-60.0)
|
||||
h.tstop = tend
|
||||
self.cell.check_all_mechs()
|
||||
while h.t < h.tstop:
|
||||
h.fadvance()
|
||||
self.voltage_traces.append(self["v_soma"])
|
||||
self.current_traces.append(self["i_inj"])
|
||||
self.time_values = np.array(self["time"])
|
||||
|
||||
def steady_im(self, window=0.1):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
window : float (default: 0.1)
|
||||
fraction of window to use for steady-state measurement, taken
|
||||
immediately before the end of the step
|
||||
Returns
|
||||
-------
|
||||
steady-state membrane current for each trace.
|
||||
"""
|
||||
Im = self.current_traces
|
||||
steps = len(Im)
|
||||
steadyStop = int((self.durs[0] + self.durs[1]) / self.dt)
|
||||
steadyStart = int(steadyStop - (self.durs[1] * window) / self.dt)
|
||||
Isteady = [Im[i][steadyStart:steadyStop].mean() for i in range(steps)]
|
||||
return np.array(Isteady)
|
||||
|
||||
def peak_im(self, window=0.4):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
window: float (default=0.4)
|
||||
fraction of window to use for peak measurement, taken
|
||||
immediately following the beginning of the step
|
||||
Returns
|
||||
------
|
||||
steady-state membrane current for each trace.
|
||||
"""
|
||||
Im = self.current_traces
|
||||
steps = len(Im)
|
||||
peakStop = int((self.durs[0] + window * self.durs[1]) / self.dt)
|
||||
peakStart = int(self.durs[0] / self.dt)
|
||||
Vhold = self.amps[
|
||||
0
|
||||
] # np.mean([self.voltage_traces[i][:peakStart].mean() for i in range(steps)])
|
||||
Ipeak = []
|
||||
for i in range(steps):
|
||||
if self.voltage_cmd[i] > Vhold:
|
||||
Ipeak.append(Im[i][peakStart:peakStop].max())
|
||||
else:
|
||||
Ipeak.append(Im[i][peakStart:peakStop].min())
|
||||
return np.array(Ipeak)
|
||||
|
||||
def show(self, cell=None):
|
||||
"""
|
||||
Plot results from run_iv()
|
||||
"""
|
||||
if not HAVE_PG:
|
||||
raise Exception("Requires pyqtgraph")
|
||||
|
||||
#
|
||||
# Generate figure with subplots
|
||||
#
|
||||
app = pg.mkQApp()
|
||||
if cell is not None:
|
||||
win = pg.GraphicsWindow(
|
||||
"%s %s (%s)"
|
||||
% (
|
||||
cell.status["name"],
|
||||
cell.status["modelType"],
|
||||
cell.status["species"],
|
||||
)
|
||||
)
|
||||
else:
|
||||
win = pg.GraphisWindow("Voltage Clamp")
|
||||
self.win = win
|
||||
win.resize(1000, 800)
|
||||
Iplot = win.addPlot(labels={"left": "Im (nA)", "bottom": "Time (ms)"})
|
||||
rightGrid = win.addLayout(rowspan=2)
|
||||
win.nextRow()
|
||||
Vplot = win.addPlot(labels={"left": "V (mV)", "bottom": "Time (ms)"})
|
||||
|
||||
IVplot = rightGrid.addPlot(labels={"left": "Vm (mV)", "bottom": "Icmd (nA)"})
|
||||
IVplot.showGrid(x=True, y=True)
|
||||
rightGrid.nextRow()
|
||||
|
||||
win.ci.layout.setRowStretchFactor(0, 10)
|
||||
win.ci.layout.setRowStretchFactor(1, 5)
|
||||
|
||||
#
|
||||
# Plot simulation and analysis results
|
||||
#
|
||||
Vm = self.voltage_traces
|
||||
Iinj = self.current_traces
|
||||
Vcmd = self.voltage_cmd
|
||||
t = self.time_values
|
||||
steps = len(Vcmd)
|
||||
|
||||
# plot I, V traces
|
||||
colors = [(i, steps * 3.0 / 2.0) for i in range(steps)]
|
||||
for i in range(steps):
|
||||
Vplot.plot(t, Vm[i], pen=colors[i])
|
||||
Iplot.plot(t, Iinj[i], pen=colors[i])
|
||||
|
||||
# I/V relationships
|
||||
IVplot.plot(Vcmd, self.peak_im(), symbol="o", symbolBrush=(50, 150, 50, 255))
|
||||
IVplot.plot(Vcmd, self.steady_im(), symbol="s")
|
||||
Reference in New Issue
Block a user