model of DCN pyramidal neuron
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.

483 lines
19 KiB

from __future__ import print_function
import numpy as np
from neuron import h
from .cell import Cell
from .. import data
from ..util import Params
from ..util import nstomho
__all__ = ["Pyramidal", "PyramidalKanold"]
class Pyramidal(Cell):
type = "pyramidal"
@classmethod
def create(cls, model="POK", **kwds):
if model == "POK":
return PyramidalKanold(**kwds)
else:
raise ValueError("Pyramidal model %s is unknown", model)
def make_psd(self, terminal, psd_type, **kwds):
"""
Connect a presynaptic terminal to one post section at the specified location, with the fraction
of the "standard" conductance determined by gbar.
The default condition is to try to pass the default unit test (loc=0.5)
Parameters
----------
terminal : Presynaptic terminal (NEURON object)
psd_type : either simple or multisite PSD for bushy cell
kwds: dict of options. Two are currently handled:
postsize : expect a list consisting of [sectionno, location (float)]
AMPAScale : float to scale the ampa currents
"""
if (
"postsite" in kwds
): # use a defined location instead of the default (soma(0.5)
postsite = kwds["postsite"]
loc = postsite[1] # where on the section?
uname = (
"sections[%d]" % postsite[0]
) # make a name to look up the neuron section object
post_sec = self.hr.get_section(uname) # Tell us where to put the synapse.
else:
loc = 0.5
post_sec = self.soma
if psd_type == "simple":
if terminal.cell.type in [
"sgc",
"dstellate",
"tuberculoventral",
"cartwheel",
]:
weight = data.get(
"%s_synapse" % terminal.cell.type,
species=self.species,
post_type=self.type,
field="weight",
)
tau1 = data.get(
"%s_synapse" % terminal.cell.type,
species=self.species,
post_type=self.type,
field="tau1",
)
tau2 = data.get(
"%s_synapse" % terminal.cell.type,
species=self.species,
post_type=self.type,
field="tau2",
)
erev = data.get(
"%s_synapse" % terminal.cell.type,
species=self.species,
post_type=self.type,
field="erev",
)
return self.make_exp2_psd(
post_sec,
terminal,
weight=weight,
loc=loc,
tau1=tau1,
tau2=tau2,
erev=erev,
)
else:
raise TypeError(
"Cannot make simple PSD for %s => %s"
% (terminal.cell.type, self.type)
)
elif psd_type == "multisite":
if terminal.cell.type == "sgc":
# Max conductances for the glu mechanisms are calibrated by
# running `synapses/tests/test_psd.py`. The test should fail
# if these values are incorrect
self.AMPAR_gmax = (
data.get(
"sgc_synapse",
species=self.species,
post_type=self.type,
field="AMPAR_gmax",
)
* 1e3
)
self.NMDAR_gmax = (
data.get(
"sgc_synapse",
species=self.species,
post_type=self.type,
field="NMDAR_gmax",
)
* 1e3
)
self.Pr = data.get(
"sgc_synapse", species=self.species, post_type=self.type, field="Pr"
)
# adjust gmax to correct for initial Pr
self.AMPAR_gmax = self.AMPAR_gmax / self.Pr
self.NMDAR_gmax = self.NMDAR_gmax / self.Pr
if "AMPAScale" in kwds:
self.AMPA_gmax = (
self.AMPA_gmax * kwds["AMPAScale"]
) # allow scaling of AMPA conductances
if "NMDAScale" in kwds:
self.NMDA_gmax = self.NMDA_gmax * kwds["NMDAScale"]
return self.make_glu_psd(
post_sec, terminal, self.AMPAR_gmax, self.NMDAR_gmax, loc=loc
)
elif terminal.cell.type == "dstellate": # WBI input -Voigt, Nelken, Young
return self.make_gly_psd(post_sec, terminal, psdtype="glyfast", loc=loc)
elif (
terminal.cell.type == "tuberculoventral"
): # TV cells talk to each other-Kuo et al.
return self.make_gly_psd(post_sec, terminal, psdtype="glyfast", loc=loc)
else:
raise TypeError(
"Cannot make PSD for %s => %s" % (terminal.cell.type, self.type)
)
else:
raise ValueError("Unsupported psd type %s" % psd_type)
class PyramidalKanold(Pyramidal, Cell):
"""
DCN pyramidal cell
Kanold and Manis, 1999, 2001, 2005
"""
def __init__(
self,
morphology=None,
decorator=None,
nach=None,
ttx=False,
species="rat",
modelType=None,
debug=False,
):
"""
initialize a pyramidal cell, based on the Kanold-Manis (2001) pyramidal cell model.
Modifications to the cell can be made by calling methods below. These include
converting to a model with modified size and conductances (experimental).
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 is inserted into the first soma section, and the
rest of the structure is "bare".
nach : string (default: None)
nach selects the type of sodium channel that will be used in the model. A channel mechanim
by that name must exist. None implies the default channel, 'napyr'.
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 (overridden by decorator).
modelType: string (default: None)
modelType specifies the type of the model that will be used (e.g., "II", "II-I", etc).
modelType is passed to the decorator, or to species_scaling to adjust point models.
debug: boolean (default: False)
debug is a boolean flag. When set, there will be multiple printouts of progress and parameters.
Returns
-------
Nothing
"""
super(PyramidalKanold, self).__init__()
if modelType == None:
modelType = "POK"
if nach == None:
nach = "napyr"
self.status = {
"soma": True,
"axon": False,
"dendrites": False,
"pumps": False,
"na": nach,
"species": species,
"modelType": modelType,
"ttx": ttx,
"name": "Pyramidal",
"morphology": morphology,
"decorator": decorator,
"temperature": None,
}
self.i_test_range = {"pulse": (-0.3, 0.401, 0.02)}
self.vrange = [-75.0, -60.0]
if morphology is None:
"""
instantiate a basic soma-only ("point") model
"""
soma = h.Section(
name="Pyramidal_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 = [
"napyr",
"kdpyr",
"kif",
"kis",
"ihpyr",
"leak",
"kcnq",
"nap",
]
for mech in self.mechanisms:
try:
self.soma.insert(mech)
except ValueError:
print("WARNING: Mechanism %s not found" % mech)
self.soma().kif.kif_ivh = -89.6
self.species_scaling(
silent=True, species=species, modelType=modelType
) # set the default type I-c 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("<< PYR: POK Pyramidal Cell created >>")
def get_cellpars(self, dataset, species="guineapig", celltype="II"):
cellcap = data.get(
dataset, species=species, cell_type=celltype, field="soma_Cap"
)
chtype = data.get(
dataset, species=species, cell_type=celltype, field="soma_natype"
)
pars = Params(cap=cellcap, natype=chtype)
for g in [
"soma_napyr_gbar",
"soma_kdpyr_gbar",
"soma_kif_gbar",
"soma_kis_gbar",
"soma_kcnq_gbar",
"soma_nap_gbar",
"soma_ihpyr_gbar",
"soma_leak_gbar",
"soma_e_h",
"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, species="rat", modelType="I", silent=True):
"""
Adjust all of the conductances and the cell size according to the species requested.
Used ONLY for point models.
Parameters
----------
species : string (default: 'rat')
name of the species to use for scaling the conductances in the base point model
Must be 'rat'
modelType: string (default: 'I')
definition of model type from Kanold and Manis, 2001
choices are 'I' or 'POK' (canonical model) or
'II', a modified model with more physiological surface area and KCNQ channels
silent : boolean (default: True)
run silently (True) or verbosely (False)
"""
if modelType in ["I", "POK"]:
celltype = "pyramidal"
elif modelType in ["II"]:
celltype = "pyramidal-II"
else:
celltype = modelType
dataset = "POK_channels"
soma = self.soma
if species in ["rat", "mouse"] and modelType in [
"I",
"POK",
"II",
]: # canonical K&M2001 model cell
self._valid_temperatures = (34.0,)
if self.status["temperature"] is None:
self.set_temperature(34.0)
pars = self.get_cellpars(dataset, species=species, celltype=celltype)
self.set_soma_size_from_Cm(pars.cap)
self.status["na"] = pars.natype
soma().napyr.gbar = nstomho(pars.soma_napyr_gbar, self.somaarea)
soma().nap.gbar = nstomho(
pars.soma_nap_gbar, self.somaarea
) # does not exist in canonical model
soma().kdpyr.gbar = nstomho(pars.soma_kdpyr_gbar, self.somaarea)
soma().kcnq.gbar = nstomho(
pars.soma_kcnq_gbar, self.somaarea
) # does not exist in canonical model.
soma().kif.gbar = nstomho(pars.soma_kif_gbar, self.somaarea)
soma().kis.gbar = nstomho(pars.soma_kis_gbar, self.somaarea)
soma().ihpyr.gbar = nstomho(pars.soma_ihpyr_gbar, self.somaarea)
# soma().ihpyr_adj.q10 = 3.0 # no temp scaling to sta
soma().leak.gbar = nstomho(pars.soma_leak_gbar, self.somaarea)
soma().leak.erev = pars.soma_leak_erev
soma().ena = pars.soma_e_na
soma().ek = pars.soma_e_k
soma().ihpyr.eh = pars.soma_e_h
# elif species in 'rat' and modelType == 'II':
# """
# Modified canonical K&M2001 model cell
# In this model version, the specific membrane capacitance is modified
# so that the overall membrane time constant is consistent with experimental
# measures in slices. However, this is not a physiological value. Attempts
# to use the normal 1 uF/cm2 value were unsuccessful in establishing the expected
# ~12 msec time constant.
# This model also adds a KCNQ channel, as described by Li et al., 2012.
# """
# self.c_m = 6.0
# self.set_soma_size_from_Diam(30.0)
# # self.set_soma_size_from_Cm(80.0)
# # print 'diameter: %7.1f' % self.soma.diam
# self._valid_temperatures = (34.,)
# if self.status['temperature'] is None:
# self.set_temperature(34.)
# self.refarea = self.somaarea
# soma().napyr.gbar = nstomho(550, self.refarea)
# soma().nap.gbar = nstomho(60.0, self.refarea)
# soma().kcnq.gbar = nstomho(2, self.refarea) # pyramidal cells have kcnq: Li et al, 2011 (Thanos)
# soma().kdpyr.gbar = nstomho(180, self.refarea) # Normally 80.
# soma().kif.gbar = nstomho(150, self.refarea) # normally 150
# soma().kis.gbar = nstomho(40, self.refarea) # 40
# soma().ihpyr.gbar = nstomho(2.8, self.refarea)
# soma().leak.gbar = nstomho(0.5, self.refarea)
# soma().leak.erev = -62. # override default values in cell.py
# soma().ena = 50.0
# soma().ek = -81.5
# soma().ihpyr.eh = -43
# if not self.status['dendrites']:
# self.add_dendrites()
else:
raise ValueError(
"Species %s or species-modelType %s is not implemented for Pyramidal cells"
% (species, modelType)
)
self.status["species"] = species
self.status["modelType"] = modelType
# self.cell_initialize(showinfo=True)
self.check_temperature()
if not silent:
print("set cell as: ", species, modelType)
print(" with Vm rest = %f" % self.vm0)
print(self.status)
for m in self.mechanisms:
print("%s.gbar = %f" % (m, eval("soma().%s.gbar" % m)))
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
Overrides i_currents in cells.py because we have a different set of currents
to compute.
"""
for part in self.all_sections.keys():
for sec in self.all_sections[part]:
sec.v = V
h.celsius = self.status["temperature"]
h.finitialize()
self.ix = {}
if "napyr" in self.mechanisms:
self.ix["napyr"] = self.soma().napyr.gna * (V - self.soma().ena)
if "nap" in self.mechanisms:
self.ix["nap"] = self.soma().nap.gnap * (V - self.soma().ena)
if "kdpyr" in self.mechanisms:
self.ix["kdpyr"] = self.soma().kdpyr.gk * (V - self.soma().ek)
if "kif" in self.mechanisms:
self.ix["kif"] = self.soma().kif.gkif * (V - self.soma().ek)
if "kis" in self.mechanisms:
self.ix["kis"] = self.soma().kis.gkis * (V - self.soma().ek)
if "kcnq" in self.mechanisms:
self.ix["kcnq"] = self.soma().kcnq.gk * (V - self.soma().ek)
if "ihpyr" in self.mechanisms:
self.ix["ihpyr"] = self.soma().ihpyr.gh * (V - self.soma().ihpyr.eh)
if "ihpyr_adj" in self.mechanisms:
self.ix["ihpyr_adj"] = self.soma().ihpyr_adj.gh * (
V - self.soma().ihpyr_adj.eh
)
# leak
if "leak" in self.mechanisms:
self.ix["leak"] = self.soma().leak.gbar * (V - self.soma().leak.erev)
return np.sum([self.ix[i] for i in self.ix])
def add_dendrites(self):
"""
Add simple unbranched dendrite.
The dendrites have some kd, kif and ih current
"""
nDend = range(2) # these will be simple, unbranced, N=4 dendrites
dendrites = []
for i in nDend:
dendrites.append(h.Section(cell=self.soma))
for i in nDend:
dendrites[i].connect(self.soma)
dendrites[i].L = 250 # length of the dendrite (not tapered)
dendrites[i].diam = 1
dendrites[i].cm = self.c_m
# h('dendrites[i].diam(0:1) = 2:1') # dendrite diameter, with tapering
dendrites[i].nseg = 21 # # segments in dendrites
dendrites[i].Ra = 150 # ohm.cm
dendrites[i].insert("napyr")
dendrites[i]().napyr.gbar = 0.00
dendrites[i].insert("kdpyr")
dendrites[i]().kdpyr.gbar = 0.002 # a little Ht
dendrites[i].insert("kif")
dendrites[i]().kif.gbar = 0.0001 # a little Ht
dendrites[i].insert("leak") # leak
dendrites[i]().leak.gbar = 0.00001
dendrites[i].insert("ihpyr_adj") # some H current
# mechanism missing so the ihvcn mechanism need to be inserted
dendrites[i].insert('ihvcn')
dendrites[i]().ihvcn.gbar = 0.0 # 0.00002
dendrites[i]().ihvcn.eh = -43.0
self.maindend = dendrites
self.status["dendrites"] = True
self.add_section(self.maindend, "maindend")