copying to personal repo

This commit is contained in:
Alan
2022-06-19 13:45:53 -05:00
commit bf2ffa7315
287 changed files with 54032 additions and 0 deletions

View 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
View 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

View 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()

View 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())

View 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"])

View 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)

View 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)

View 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()

View 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")