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
483 lines
19 KiB
2 years ago
|
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")
|