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