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.
218 lines
6.2 KiB
218 lines
6.2 KiB
from __future__ import print_function |
|
|
|
""" |
|
Wrapper classes to make working with NEURON easier. |
|
|
|
Author: Andrew P. Davison, UNIC, CNRS |
|
""" |
|
|
|
__version__ = "0.3.0" |
|
|
|
from neuron import nrn, h, hclass |
|
|
|
h.load_file("stdrun.hoc") |
|
|
|
PROXIMAL = 0 |
|
DISTAL = 1 |
|
|
|
|
|
class Mechanism(object): |
|
""" |
|
Examples: |
|
>>> leak = Mechanism('pas', {'e': -65, 'g': 0.0002}) |
|
>>> hh = Mechanism('hh') |
|
added set_parameters to allow post-instantiation parameter modification |
|
""" |
|
|
|
def __init__(self, name, **parameters): |
|
self.name = name |
|
self.parameters = parameters |
|
|
|
def set_parameters(self, parameters): |
|
self.parameters = parameters |
|
|
|
def insert_into(self, section): |
|
section.insert(self.name) |
|
for name, value in self.parameters.items(): |
|
for segment in section: |
|
mech = getattr(segment, self.name) |
|
setattr(mech, name, value) |
|
|
|
|
|
class Section(nrn.Section): |
|
""" |
|
Examples: |
|
>>> soma = Section(L=30, diam=30, mechanisms=[hh, leak]) |
|
>>> apical = Section(L=600, diam=2, nseg=5, mechanisms=[leak], |
|
... parent=soma, connection_point=DISTAL) |
|
""" |
|
|
|
def __init__( |
|
self, |
|
L, |
|
diam, |
|
nseg=1, |
|
Ra=100, |
|
cm=1, |
|
mechanisms=[], |
|
parent=None, |
|
connection_point=DISTAL, |
|
): |
|
nrn.Section.__init__(self) |
|
# set geometry |
|
self.L = L |
|
self.diam = diam |
|
self.nseg = nseg |
|
# set cable properties |
|
self.Ra = Ra |
|
self.cm = cm |
|
# connect to parent section |
|
if parent: |
|
self.connect(parent, connection_point, PROXIMAL) |
|
# add ion channels |
|
for mechanism in mechanisms: |
|
mechanism.insert_into(self) |
|
|
|
def add_synapses(self, label, type, locations=[0.5], **parameters): |
|
if hasattr(self, label): |
|
raise Exception("Can't overwrite synapse labels (to keep things simple)") |
|
synapse_group = [] |
|
for location in locations: |
|
synapse = getattr(h, type)(location, sec=self) |
|
for name, value in parameters.items(): |
|
setattr(synapse, name, value) |
|
synapse_group.append(synapse) |
|
if len(synapse_group) == 1: |
|
synapse_group = synapse_group[0] |
|
setattr(self, label, synapse_group) |
|
|
|
add_synapse = add_synapses # for backwards compatibility |
|
|
|
def plot(self, variable, location=0.5, tmin=0, tmax=5, xmin=-80, xmax=40): |
|
import neuron.gui |
|
|
|
self.graph = h.Graph() |
|
h.graphList[0].append(self.graph) |
|
self.graph.size(tmin, tmax, xmin, xmax) |
|
self.graph.addvar("%s(%g)" % (variable, location), sec=self) |
|
|
|
def record_spikes(self, threshold=-30): |
|
self.spiketimes = h.Vector() |
|
self.spikecount = h.APCount(0.5, sec=self) |
|
self.spikecount.thresh = threshold |
|
self.spikecount.record(self.spiketimes) |
|
|
|
|
|
def alias(attribute_path): |
|
""" |
|
Returns a new property, mapping an attribute nested in an object hierarchy |
|
to a simpler name |
|
|
|
For example, suppose that an object of class A has an attribute b which |
|
itself has an attribute c which itself has an attribute d. Then placing |
|
e = alias('b.c.d') |
|
in the class definition of A makes A.e an alias for A.b.c.d |
|
""" |
|
|
|
parts = attribute_path.split(".") |
|
attr_name = parts[-1] |
|
attr_path = parts[:-1] |
|
|
|
def set(self, value): |
|
obj = reduce(getattr, [self] + attr_path) |
|
setattr(obj, attr_name, value) |
|
|
|
def get(self): |
|
obj = reduce(getattr, [self] + attr_path) |
|
return getattr(obj, attr_name) |
|
|
|
return property(fset=set, fget=get) |
|
|
|
|
|
def uniform_property(section_list, attribute_path): |
|
""" |
|
Define a property that will have a uniform value across a list of sections. |
|
|
|
For example, suppose we define a neuron model as a class A, which contains |
|
three compartments: soma, dendrite and axon. Then placing |
|
|
|
gnabar = uniform_property(["soma", "axon"], "hh.gnabar") |
|
|
|
in the class definition of A means that setting a.gnabar (where a is an |
|
instance of A) will set the value of hh.gnabar in both the soma and axon, i.e. |
|
|
|
a.gnabar = 0.01 |
|
|
|
is equivalent to: |
|
|
|
for sec in [a.soma, a.axon]: |
|
for seg in sec: |
|
seg.hh.gnabar = 0.01 |
|
|
|
""" |
|
parts = attribute_path.split(".") |
|
attr_name = parts[-1] |
|
attr_path = parts[:-1] |
|
|
|
def set(self, value): |
|
for sec_name in section_list: |
|
sec = getattr(self, sec_name) |
|
for seg in sec: |
|
obj = reduce(getattr, [seg] + attr_path) |
|
setattr(obj, attr_name, value) |
|
|
|
def get(self): |
|
sec = getattr(self, section_list[0]) |
|
obj = reduce(getattr, [sec(0.5)] + attr_path) |
|
return getattr(obj, attr_name) |
|
|
|
return property(fset=set, fget=get) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
class SimpleNeuron(object): |
|
def __init__(self): |
|
# define ion channel parameters |
|
leak = Mechanism("pas", e=-65, g=0.0002) |
|
hh = Mechanism("hh") |
|
# create cable sections |
|
self.soma = Section(L=30, diam=30, mechanisms=[hh]) |
|
self.apical = Section( |
|
L=600, |
|
diam=2, |
|
nseg=5, |
|
mechanisms=[leak], |
|
parent=self.soma, |
|
connection_point=DISTAL, |
|
) |
|
self.basilar = Section( |
|
L=600, |
|
diam=2, |
|
nseg=5, |
|
mechanisms=[leak], |
|
parent=self.soma, |
|
connection_point=0.5, |
|
) |
|
self.axon = Section( |
|
L=1000, diam=1, nseg=37, mechanisms=[hh], connection_point=0 |
|
) |
|
# synaptic input |
|
self.soma.add_synapses("syn", "AlphaSynapse", onset=0.5, gmax=0.05, e=0) |
|
|
|
gnabar = uniform_property(["soma", "axon"], "hh.gnabar") |
|
gkbar = uniform_property(["soma", "axon"], "hh.gkbar") |
|
|
|
neuron = SimpleNeuron() |
|
neuron.soma.plot("v") |
|
neuron.apical.plot("v") |
|
|
|
print("gNa_bar: ", neuron.gnabar) |
|
neuron.gnabar = 0.15 |
|
assert neuron.soma(0.5).hh.gnabar == 0.15 |
|
|
|
h.dt = 0.025 |
|
v_init = -65 |
|
tstop = 5 |
|
h.finitialize(v_init) |
|
h.run()
|
|
|