Source code for patch.objects

from .core import transform, transform_record, _is_sequence


[docs]class PythonHocObject: def __init__(self, interpreter, ptr): # Initialize ourselves with a reference to our own "pointer" # and prepare a list for other references. self._neuron_ptr = transform(ptr) self._references = [] self._interpreter = interpreter def __getattr__(self, attr): # Return underlying attributes that aren't explicitly set on the wrapper return getattr(self.__dict__["_neuron_ptr"], attr) def __setattr__(self, attr, value): # Set attributes on the underlying pointer, and set on self if they don't # exist on the underlying pointer. This allows you to set arbitrary values # on the NEURON objects as you would be able to with a real Pythonic object. try: setattr(self._neuron_ptr, attr, value) except (LookupError, AttributeError) as _: self.__dict__[attr] = value def __call__(self, *args, **kwargs): # Relay calls to self to the underlying pointer return self._neuron_ptr(*args, **kwargs) def __iter__(self): # Create an iterator from ourselves. ptr = self.__neuron__() if type(ptr).__name__ == "Section": # Iter on section isn't a full iterator. return ptr # Relay iteration to the underlying pointer try: return iter(ptr) except TypeError as _: raise def __neuron__(self): # Magic method that allows duck typing of this object as something that # needs to be represented differently when passed to NEURON. return self._neuron_ptr def __ref__(self, obj): # Magic method that will store a strong reference to another object. if obj not in self._references: self._references.append(obj) def __deref__(self, obj): # Magic method that will remove a strong reference to another object. try: self._references.remove(obj) return True except ValueError as _: return False
[docs]class connectable: def __init__(self): # Prepare a dictionary that lists which other NEURON parts this is connected to self._connections = {}
[docs]class Section(PythonHocObject, connectable): def __init__(self, *args, **kwargs): PythonHocObject.__init__(self, *args, **kwargs) connectable.__init__(self)
[docs] def connect(self, target, *args, **kwargs): nrn_target = transform(target) self.__neuron__().connect(nrn_target, *args, **kwargs) if hasattr(target, "__ref__"): target.__ref__(self) self.__ref__(target)
def __arc__(self): return 0.5 def __netcon__(self): return self(self.__arc__()).__netcon__() def __record__(self): return self(self.__arc__()).__netcon__() def __call__(self, *args, **kwargs): v = super().__call__(*args, **kwargs) if type(v).__name__ != "Segment": # pragma: no cover raise TypeError("Section call did not return a Segment.") return Segment(self._interpreter, v) def __iter__(self, *args, **kwargs): iter = super().__iter__(*args, **kwargs) for v in iter: if type(v).__name__ != "Segment": # pragma: no cover raise TypeError("Section iteration did not return a Segment.") yield Segment(self._interpreter, v)
[docs] def insert(self, *args, **kwargs): # Catch nrn.Section return value, always seems to be self. # So if Neuron doesn't raise an error, return self. # Probably for method chaining? self.__neuron__().insert(*args, **kwargs) return self
[docs] def connect_points(self, target, x=None): if x is None: x = self.__arc__() segment = self(x) self.push() self._interpreter.NetCon(segment, target) self._interpreter.pop_section()
[docs] def set_dimensions(self, length, diameter): self.L = length self.diam = diameter
[docs] def set_segments(self, segments): self.nseg = segments
[docs] def add_3d(self, points, diameters=None): """ Add a new 3D point to this section xyz data. :param points: A 2D array of xyz points. :param diameters: A scalar or array of diameters corresponding to the points. Default value is the section diameter. :type diameters: float or array """ if diameters is None: diameters = [self.diam for _ in range(len(points))] if not _is_sequence(diameters): diameters = [diameter for _ in range(len(points))] self.__neuron__().push() for point, diameter in zip(points, diameters): self._interpreter.pt3dadd(*point, diameter) self._interpreter.pop_section()
[docs] def wholetree(self): return [Section(self._interpreter, s) for s in self.__neuron__().wholetree()]
[docs] def record(self, x=None): if x is None: x = self.__arc__() if not hasattr(self, "recordings"): self.recordings = {} if not x in self.recordings: recorder = self._interpreter.Vector() recorder.record(self(x)) self.recordings[x] = recorder return recorder
[docs]class Vector(PythonHocObject):
[docs] def record(self, target, *args, **kwargs): nrn_target = transform_record(target) self.__neuron__().record(nrn_target, *args, **kwargs) self.__ref__(target)
[docs]class NetStim(PythonHocObject, connectable): def __init__(self, *args, **kwargs): PythonHocObject.__init__(self, *args, **kwargs) connectable.__init__(self)
[docs]class VecStim(PythonHocObject, connectable): def __init__(self, *args, **kwargs): PythonHocObject.__init__(self, *args, **kwargs) connectable.__init__(self) @property def vector(self): return self._vector @property def pattern(self): return self._pattern.copy()
[docs]class NetCon(PythonHocObject): pass
[docs]class Segment(PythonHocObject, connectable): def __init__(self, *args, **kwargs): PythonHocObject.__init__(self, *args, **kwargs) connectable.__init__(self) def __netcon__(self): return self.__neuron__()._ref_v def __record__(self): return self.__neuron__()._ref_v
[docs]class PointProcess(PythonHocObject, connectable): """ Wrapper for all point processes (membrane and synapse mechanisms). Use ``PythonHocInterpreter.PointProcess`` to construct these objects. """ def __init__(self, *args, **kwargs): PythonHocObject.__init__(self, *args, **kwargs) connectable.__init__(self)
[docs] def stimulate(self, pattern=None, **kwargs): if pattern is None: # No specific pattern given, create NetStim stimulus = self._interpreter.NetStim() for kw, value in kwargs.items(): setattr(stimulus.__neuron__(), kw, value) else: # Specific pattern required, create VecStim stimulus = self._interpreter.VecStim(pattern=pattern) self._interpreter.NetCon(stimulus, self) return stimulus