You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
468 lines
17 KiB
468 lines
17 KiB
2 years ago
|
from __future__ import print_function
|
||
|
from neuron import h
|
||
|
from ..util import nstomho
|
||
|
from ..util import Params
|
||
|
import numpy as np
|
||
|
from .cell import Cell
|
||
|
from .. import synapses
|
||
|
from .. import an_model
|
||
|
from .. import data
|
||
|
|
||
|
__all__ = ["SGC", "SGC_TypeI", "DummySGC"]
|
||
|
|
||
|
|
||
|
class SGC(Cell):
|
||
|
type = "sgc"
|
||
|
|
||
|
@classmethod
|
||
|
def create(cls, model="I", species="mouse", **kwds):
|
||
|
if model == "dummy":
|
||
|
return DummySGC(**kwds)
|
||
|
elif model == "I":
|
||
|
return SGC_TypeI(species=species, **kwds)
|
||
|
else:
|
||
|
raise ValueError("SGC model %s is unknown", model)
|
||
|
|
||
|
def __init__(self, cf=None, sr=None):
|
||
|
Cell.__init__(self)
|
||
|
self._cf = cf
|
||
|
self._sr = sr
|
||
|
self.spike_source = None # used by DummySGC to connect VecStim to terminal
|
||
|
|
||
|
@property
|
||
|
def cf(self):
|
||
|
""" Center frequency
|
||
|
"""
|
||
|
return self._cf
|
||
|
|
||
|
@property
|
||
|
def sr(self):
|
||
|
""" Spontaneous rate group. 1=low, 2=mid, 3=high
|
||
|
"""
|
||
|
return self._sr
|
||
|
|
||
|
def make_terminal(self, post_cell, term_type, **kwds):
|
||
|
"""Create a StochasticTerminal and configure it according to the
|
||
|
postsynaptic cell type.
|
||
|
"""
|
||
|
pre_sec = self.soma
|
||
|
|
||
|
# Return a simple terminal unless a stochastic terminal was requested.
|
||
|
if term_type == "simple":
|
||
|
return synapses.SimpleTerminal(
|
||
|
pre_sec, post_cell, spike_source=self.spike_source, **kwds
|
||
|
)
|
||
|
elif term_type == "multisite":
|
||
|
n_rsites = data.get(
|
||
|
"sgc_synapse",
|
||
|
species="mouse",
|
||
|
post_type=post_cell.type,
|
||
|
field="n_rsites",
|
||
|
)
|
||
|
opts = {"nzones": n_rsites, "delay": 0, "dep_flag": 1}
|
||
|
opts.update(kwds)
|
||
|
# when created, depflag is set True (1) so that we compute the DKR D*F to get release
|
||
|
# this can be modified prior to the run by setting the terminal(s) so that dep_flag is 0
|
||
|
# (no DKR: constant release probability)
|
||
|
term = synapses.StochasticTerminal(
|
||
|
pre_sec, post_cell, spike_source=self.spike_source, **opts
|
||
|
)
|
||
|
|
||
|
kinetics = data.get(
|
||
|
"sgc_ampa_kinetics",
|
||
|
species="mouse",
|
||
|
post_type=post_cell.type,
|
||
|
field=["tau_g", "amp_g"],
|
||
|
)
|
||
|
term.set_params(**kinetics)
|
||
|
dynamics = data.get(
|
||
|
"sgc_release_dynamics",
|
||
|
species="mouse",
|
||
|
post_type=post_cell.type,
|
||
|
field=["F", "k0", "kmax", "kd", "kf", "taud", "tauf", "dD", "dF"],
|
||
|
)
|
||
|
term.set_params(**dynamics)
|
||
|
return term
|
||
|
else:
|
||
|
raise ValueError("Unsupported terminal type %s" % term_type)
|
||
|
|
||
|
|
||
|
class DummySGC(SGC):
|
||
|
""" SGC class with no cell body; this cell only replays a predetermined
|
||
|
spike train.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, cf=None, sr=None, simulator=None):
|
||
|
"""
|
||
|
Parameters
|
||
|
----------
|
||
|
cf : float (default: None)
|
||
|
Required: the characteristic frequency for the SGC
|
||
|
|
||
|
sr : int (default None)
|
||
|
required : Selects the spontaneous rate group from the
|
||
|
Zilany et al (2010) model. 1 = LSR, 2 = MSR, 3 = HSR
|
||
|
|
||
|
simulator : 'cochlea' | 'matlab' | None (default None)
|
||
|
Sets the simulator interface that will be used. All models
|
||
|
currently use the Zilany et al. model, but the simulator can
|
||
|
be run though a Python-interface directly to the Matlab code
|
||
|
as publicy available, (simulator='matlab'), or can be run through
|
||
|
Rudnicki & Hemmert's Python interface to the simulator's C code
|
||
|
(simulator='cochlea').
|
||
|
|
||
|
"""
|
||
|
self._simulator = simulator
|
||
|
SGC.__init__(self, cf, sr)
|
||
|
self.vecstim = h.VecStim()
|
||
|
|
||
|
# this causes the terminal to receive events from the VecStim:
|
||
|
self.spike_source = self.vecstim
|
||
|
|
||
|
# just an empty section for holding the terminal
|
||
|
self.add_section(h.Section(), "soma")
|
||
|
self.status = {
|
||
|
"soma": True,
|
||
|
"axon": False,
|
||
|
"dendrites": False,
|
||
|
"pumps": False,
|
||
|
"na": None,
|
||
|
"species": None,
|
||
|
"modelType": "dummy",
|
||
|
"ttx": False,
|
||
|
"name": "DummysGC",
|
||
|
"morphology": None,
|
||
|
"decorator": None,
|
||
|
"temperature": None,
|
||
|
}
|
||
|
|
||
|
def set_spiketrain(self, times):
|
||
|
""" Set the times of spikes (in seconds) to be replayed by the cell.
|
||
|
"""
|
||
|
self._spiketrain = times
|
||
|
self._stvec = h.Vector(times)
|
||
|
self.vecstim.play(self._stvec)
|
||
|
|
||
|
def set_sound_stim(self, stim, seed, simulator=None):
|
||
|
""" Set the sound stimulus used to generate this cell's spike train.
|
||
|
"""
|
||
|
self._sound_stim = stim
|
||
|
spikes = self.generate_spiketrain(stim, seed, simulator)
|
||
|
self.set_spiketrain(spikes)
|
||
|
|
||
|
def generate_spiketrain(self, stim, seed, simulator=None):
|
||
|
if simulator is None:
|
||
|
simulator = self._simulator
|
||
|
spikes = an_model.get_spiketrain(
|
||
|
cf=self.cf, sr=self.sr, seed=seed, stim=stim, simulator=simulator
|
||
|
)
|
||
|
return spikes * 1000
|
||
|
|
||
|
|
||
|
class SGC_TypeI(SGC):
|
||
|
"""
|
||
|
Spiral ganglion cell model
|
||
|
|
||
|
"""
|
||
|
|
||
|
def __init__(
|
||
|
self,
|
||
|
morphology=None,
|
||
|
decorator=None,
|
||
|
nach=None,
|
||
|
ttx=False,
|
||
|
species="guineapig",
|
||
|
modelType="bm",
|
||
|
cf=None,
|
||
|
sr=None,
|
||
|
debug=False,
|
||
|
):
|
||
|
"""
|
||
|
Initialize a spiral ganglion Type I cell, based on a bushy cell model.
|
||
|
Modifications to the cell can be made by calling the methods below. These include
|
||
|
converting to a model with modified size and conductances (experimental), and
|
||
|
and changing the sodium channel conductances.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
morphology : string (default: None)
|
||
|
a file name to read the cell morphology from. If a valid file is found, a cell is constructed
|
||
|
as a cable model from the hoc file.
|
||
|
If None (default), the only a point model is made, exactly according to RM03.
|
||
|
|
||
|
decorator : Python function (default: None)
|
||
|
decorator is a function that "decorates" the morphology with ion channels according
|
||
|
to a set of rules.
|
||
|
If None, a default set of channels aer inserted into the first soma section, and the
|
||
|
rest of the structure is "bare".
|
||
|
|
||
|
nach : string (default: 'na')
|
||
|
nach selects the type of sodium channel that will be used in the model. A channel mechanim
|
||
|
by that name must exist. The default is jsrna (Rothman et al., 1993)
|
||
|
|
||
|
ttx : Boolean (default: False)
|
||
|
If ttx is True, then the sodium channel conductance is set to 0 everywhere in the cell.
|
||
|
Currently, this is not implemented.
|
||
|
|
||
|
species: string (default 'guineapig')
|
||
|
species defines the channel density that will be inserted for different models. Note that
|
||
|
if a decorator function is specified, this argument is ignored.
|
||
|
|
||
|
modelType: string (default: None)
|
||
|
modelType specifies the type of the model that will be used. SGC model know about "a" (apical)
|
||
|
and "bm" (basal-middle) models, based on Liu et al., JARO, 2014.
|
||
|
modelType is passed to the decorator, or to species_scaling to adjust point models.
|
||
|
|
||
|
cf : float (default: None)
|
||
|
The CF for the auditory nerve fiber that this SGC represents.
|
||
|
|
||
|
sr : string (default: None)
|
||
|
The spontaneous rate group to which this fiber belongs. "LS", "MS", and "HS" are known values.
|
||
|
|
||
|
debug: boolean (default: False)
|
||
|
debug is a boolean flag. When set, there will be multiple printouts of progress and parameters.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Nothing
|
||
|
|
||
|
"""
|
||
|
|
||
|
super(SGC_TypeI, self).__init__(cf=cf, sr=sr)
|
||
|
if modelType == None:
|
||
|
modelType = "bm" # modelTypes are: a (apical), bm (basal middle)
|
||
|
if nach == None:
|
||
|
nach = "jsrna"
|
||
|
self.status = {
|
||
|
"soma": True,
|
||
|
"axon": False,
|
||
|
"dendrites": False,
|
||
|
"pumps": False,
|
||
|
"na": nach,
|
||
|
"species": species,
|
||
|
"modelType": modelType,
|
||
|
"ttx": ttx,
|
||
|
"name": "SGC",
|
||
|
"morphology": morphology,
|
||
|
"decorator": decorator,
|
||
|
"temperature": None,
|
||
|
}
|
||
|
|
||
|
self.i_test_range = {
|
||
|
"pulse": [(-0.3, 0.3, 0.02), (-0.03, 0.0, 0.005)]
|
||
|
} # include finer range as well
|
||
|
self.vrange = [-75.0, -55.0]
|
||
|
if morphology is None:
|
||
|
"""
|
||
|
instantiate a basic soma-only ("point") model
|
||
|
"""
|
||
|
soma = h.Section(
|
||
|
name="SGC_Soma_%x" % id(self)
|
||
|
) # one compartment of about 29000 um2
|
||
|
soma.nseg = 1
|
||
|
self.add_section(soma, "soma")
|
||
|
else:
|
||
|
"""
|
||
|
instantiate a structured model with the morphology as specified by
|
||
|
the morphology file
|
||
|
"""
|
||
|
self.set_morphology(morphology_file=morphology)
|
||
|
|
||
|
# decorate the morphology with ion channels
|
||
|
if decorator is None: # basic model, only on the soma
|
||
|
self.mechanisms = [nach, "klt", "kht", "leak"]
|
||
|
if modelType == "a":
|
||
|
self.mechanisms.append("ihsgcApical")
|
||
|
elif modelType == "bm":
|
||
|
self.mechanisms.append("ihsgcBasalMiddle")
|
||
|
else:
|
||
|
raise ValueError("Type %s not known for SGC model" % modelType)
|
||
|
for mech in self.mechanisms:
|
||
|
self.soma.insert(mech)
|
||
|
self.soma.ek = self.e_k
|
||
|
self.soma().leak.erev = self.e_leak
|
||
|
self.species_scaling(
|
||
|
silent=True, species=species, modelType=modelType
|
||
|
) # set the default type II cell parameters
|
||
|
else: # decorate according to a defined set of rules on all cell compartments
|
||
|
self.decorate()
|
||
|
self.save_all_mechs() # save all mechanisms inserted, location and gbar values...
|
||
|
self.get_mechs(self.soma)
|
||
|
if debug:
|
||
|
print("<< SGC: Spiral Ganglion Cell created >>")
|
||
|
|
||
|
def get_cellpars(self, dataset, species="guineapig", celltype="sgc-a"):
|
||
|
cellcap = data.get(
|
||
|
dataset, species=species, cell_type=celltype, field="soma_Cap"
|
||
|
)
|
||
|
chtype = data.get(
|
||
|
dataset, species=species, cell_type=celltype, field="soma_na_type"
|
||
|
)
|
||
|
pars = Params(soma_Cap=cellcap, natype=chtype)
|
||
|
for g in [
|
||
|
"soma_na_gbar",
|
||
|
"soma_kht_gbar",
|
||
|
"soma_klt_gbar",
|
||
|
"soma_ihap_gbar",
|
||
|
"soma_ihbm_gbar",
|
||
|
"soma_ihap_eh",
|
||
|
"soma_ihbm_eh",
|
||
|
"soma_leak_gbar",
|
||
|
"soma_leak_erev",
|
||
|
"soma_e_k",
|
||
|
"soma_e_na",
|
||
|
]:
|
||
|
pars.additem(
|
||
|
g, data.get(dataset, species=species, cell_type=celltype, field=g)
|
||
|
)
|
||
|
return pars
|
||
|
|
||
|
def species_scaling(self, silent=True, species="guineapig", modelType="a"):
|
||
|
"""
|
||
|
Adjust all of the conductances and the cell size according to the species requested.
|
||
|
Used ONLY for point models.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
species : string (default: 'guineapig')
|
||
|
name of the species to use for scaling the conductances in the base point model
|
||
|
Must be one of mouse or guineapig
|
||
|
|
||
|
modelType: string (default: 'a')
|
||
|
definition of HCN model type from Liu et al. JARO 2014:
|
||
|
'a' for apical model
|
||
|
'bm' for basal-middle model
|
||
|
|
||
|
silent : boolean (default: True)
|
||
|
run silently (True) or verbosely (False)
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Nothing
|
||
|
|
||
|
Notes
|
||
|
-----
|
||
|
The 'guineapig' model uses the mouse HCN channel model, verbatim. This may not
|
||
|
be appropriate, given that the other conductances are scaled up.
|
||
|
|
||
|
"""
|
||
|
|
||
|
soma = self.soma
|
||
|
if modelType == "a":
|
||
|
celltype = "sgc-a"
|
||
|
elif modelType == "bm":
|
||
|
celltype = "sgc-bm"
|
||
|
else:
|
||
|
raise ValueError("SGC: unrecognized model type %s " % modelType)
|
||
|
|
||
|
if species == "mouse":
|
||
|
self._valid_temperatures = (34.0,)
|
||
|
if self.status["temperature"] is None:
|
||
|
self.set_temperature(34.0)
|
||
|
par = self.get_cellpars(
|
||
|
"sgc_mouse_channels", species=species, celltype=celltype
|
||
|
)
|
||
|
elif species == "guineapig":
|
||
|
# guinea pig data from Rothman and Manis, 2003, modelType II
|
||
|
self._valid_temperatures = (22.0,)
|
||
|
if self.status["temperature"] is None:
|
||
|
self.set_temperature(22.0)
|
||
|
par = self.get_cellpars(
|
||
|
"sgc_guineapig_channels", species=species, celltype=celltype
|
||
|
)
|
||
|
|
||
|
self.set_soma_size_from_Cm(par.soma_Cap)
|
||
|
self.adjust_na_chans(soma, gbar=par.soma_na_gbar)
|
||
|
soma().kht.gbar = nstomho(par.soma_kht_gbar, self.somaarea)
|
||
|
soma().klt.gbar = nstomho(par.soma_klt_gbar, self.somaarea)
|
||
|
if celltype == "sgc-a":
|
||
|
soma().ihsgcApical.gbar = nstomho(par.soma_ihap_gbar, self.somaarea)
|
||
|
soma().ihsgcApical.eh = par.soma_ihap_eh
|
||
|
elif celltype == "sgc-bm":
|
||
|
soma().ihsgcBasalMiddle.gbar = nstomho(par.soma_ihbm_gbar, self.somaarea)
|
||
|
soma().ihsgcBasalMiddle.eh = par.soma_ihbm_eh
|
||
|
else:
|
||
|
raise ValueError(
|
||
|
"Ihsgc modelType %s not recognized for species %s" % (celltype, species)
|
||
|
)
|
||
|
soma().leak.gbar = nstomho(par.soma_leak_gbar, self.somaarea)
|
||
|
soma().leak.erev = par.soma_leak_erev
|
||
|
|
||
|
self.status["species"] = species
|
||
|
self.status["modelType"] = modelType
|
||
|
self.check_temperature()
|
||
|
if not silent:
|
||
|
print("set cell as: ", species)
|
||
|
print(" with Vm rest = %f" % self.vm0)
|
||
|
|
||
|
def adjust_na_chans(self, soma, gbar=1000.0, debug=False):
|
||
|
"""
|
||
|
adjust the sodium channel conductance
|
||
|
:param soma: a soma object whose sodium channel complement will have it's
|
||
|
conductances adjusted depending on the channel type
|
||
|
:return nothing:
|
||
|
"""
|
||
|
if self.status["ttx"]:
|
||
|
gnabar = 0.0
|
||
|
else:
|
||
|
gnabar = nstomho(gbar, self.somaarea)
|
||
|
nach = self.status["na"]
|
||
|
if nach == "jsrna":
|
||
|
soma().jsrna.gbar = gnabar
|
||
|
soma.ena = self.e_na
|
||
|
if debug:
|
||
|
print("jsrna gbar: ", soma().jsrna.gbar)
|
||
|
elif nach == "nav11":
|
||
|
soma().nav11.gbar = gnabar * 0.5
|
||
|
soma.ena = self.e_na
|
||
|
soma().nav11.vsna = 4.3
|
||
|
if debug:
|
||
|
print("sgc using inva11")
|
||
|
print("nav11 gbar: ", soma().nav11.gbar)
|
||
|
elif nach in ["na", "nacn"]:
|
||
|
soma().na.gbar = gnabar
|
||
|
soma.ena = self.e_na
|
||
|
if debug:
|
||
|
print("na gbar: ", soma().na.gbar)
|
||
|
else:
|
||
|
raise ValueError("Sodium channel %s is not recognized for SGC cells", nach)
|
||
|
|
||
|
def i_currents(self, V):
|
||
|
"""
|
||
|
For the steady-state case, return the total current at voltage V
|
||
|
Used to find the zero current point
|
||
|
vrange brackets the interval
|
||
|
Implemented here are the basic RM03 mechanisms
|
||
|
This function should be replaced for specific cell types.
|
||
|
"""
|
||
|
for part in self.all_sections.keys():
|
||
|
for sec in self.all_sections[part]:
|
||
|
sec.v = V
|
||
|
|
||
|
h.t = 0.0
|
||
|
h.celsius = self.status["temperature"]
|
||
|
h.finitialize()
|
||
|
self.ix = {}
|
||
|
if "na" in self.mechanisms:
|
||
|
# print dir(self.soma().na)
|
||
|
self.ix["na"] = self.soma().na.gna * (V - self.soma().ena)
|
||
|
if "jsrna" in self.mechanisms:
|
||
|
# print dir(self.soma().na)
|
||
|
self.ix["jsrna"] = self.soma().jsrna.gna * (V - self.soma().ena)
|
||
|
if "klt" in self.mechanisms:
|
||
|
self.ix["klt"] = self.soma().klt.gklt * (V - self.soma().ek)
|
||
|
if "kht" in self.mechanisms:
|
||
|
self.ix["kht"] = self.soma().kht.gkht * (V - self.soma().ek)
|
||
|
if "ihsgcApical" in self.mechanisms:
|
||
|
self.ix["ihsgcApical"] = self.soma().ihsgcApical.gh * (
|
||
|
V - self.soma().ihsgcApical.eh
|
||
|
)
|
||
|
if "ihsgcBasalMiddle" in self.mechanisms:
|
||
|
self.ix["ihsgcBasalMiddle"] = self.soma().ihsgcBasalMiddle.gh * (
|
||
|
V - self.soma().ihsgcBasalMiddle.eh
|
||
|
)
|
||
|
if "leak" in self.mechanisms:
|
||
|
self.ix["leak"] = self.soma().leak.gbar * (V - self.soma().leak.erev)
|
||
|
# print self.status['name'], self.status['type'], V, self.ix
|
||
|
return np.sum([self.ix[i] for i in self.ix])
|