Source code for olfactorybulb.neuronunit.models.neuron_cell

import neuronunit
import quantities as pq
import sciunit
from neo import AnalogSignal

import olfactorybulb
from olfactorybulb.neuronunit.tests.utilities import get_zero_crossings_neg2pos

# MOCKS for autodoc
import quantities as pq
if pq.__module__ == 'sphinx.ext.autodoc.mock':
    pq.nA = pq.mV = pq.ms = pq.Hz = 1
# END MOCKS

[docs]class NeuronCellModel(sciunit.Model, sciunit.capabilities.Runnable, neuronunit.capabilities.ReceivesSquareCurrent, neuronunit.capabilities.ProducesMembranePotential, neuronunit.capabilities.ProducesSpikes, olfactorybulb.neuronunit.capabilities.SupportsVoltageClamp, olfactorybulb.neuronunit.capabilities.SupportsSettingStopTime, olfactorybulb.neuronunit.capabilities.SupportsSettingTemperature): """ Defines a NeuronUnit model for running NeuronUnit tests against a cell model (1+ sections) implemented in NEURON simulator. The class implements methods to inject current and record membrane potential at specified cell segments. The class assumes the NEURON model has been loaded, synaptically isolated, and ready for current injection experiments. As input, it takes references to the NEURON segments where current is to be injected and membrane voltage measured. IMPORTANT: When modifying this class, ensure all unit tests pass before checking in your changes to prevent breaking of dependent NeuronUnit tests. Usage: # Load and setup your model in NEURON first from neuron import h h.load_file('cell.hoc') soma = h.Cell[0].soma dendrite = h.Cell[0].dend # Pass the segment where the current will be injected, and where the membrane potential will be measured model = NeuronCellModel(in_seg=soma(0.5), out_seg=dendrite(1.0), name="Smith et. al. (1996) Random Cell") # Judge the model test.judge(model) """ default_sampling_period = 1 # ms
[docs] def __init__(self, in_seg, out_seg=None, name=None): super(NeuronCellModel, self).__init__() self.name = name self.in_seg = in_seg self.out_seg = out_seg if out_seg else in_seg from neuron import h self.h = h # Set up current and voltage clamps self.injector = self.h.IClamp(self.in_seg) self.vclamp = self.h.SEClamp(self.in_seg) # Setup recorders and clear clamps self.reset_instruments()
[docs] def reset_instruments(self): ''' Resets the current and voltage clamps and Vector recorders ''' # Reset current clamp self.injector.delay = 0 self.injector.dur = 0 self.injector.amp = 0 # Reset voltage clamp self.vclamp.amp1, self.vclamp.amp2, self.vclamp.amp3 = [0,0,0] self.vclamp.dur1, self.vclamp.dur2, self.vclamp.dur3 = [0,0,0] self.vclamp.rs = 0.001 # MOhm # Reset any changes to sampling period self.setup_recorders(self.default_sampling_period)
[docs] def setup_recorders(self, sampling_period): self.sampling_period = sampling_period self.h.steps_per_ms = round(1.0 / self.sampling_period) vec_buff_size = 10000*self.h.steps_per_ms # Set up recorders for simulation time and membrane voltage self.tVector = self.h.Vector() self.vVector = self.h.Vector() self.vciVector = self.h.Vector() # Allocate 10s worth of recording space - to avoid resizing self.vVector.buffer_size(vec_buff_size) self.tVector.buffer_size(vec_buff_size) self.vciVector.buffer_size(vec_buff_size) self.vVector.record(self.out_seg._ref_v, self.sampling_period) self.tVector.record(self.h._ref_t, self.sampling_period) self.vciVector.record(self.vclamp._ref_i, self.sampling_period)
[docs] def get_backend(self): return self
[docs] def set_stop_time(self, tstop): self.h.tstop = tstop.rescale(pq.ms).magnitude
[docs] def set_temperature(self, celsius): self.h.celsius = celsius
[docs] def inject_square_current(self, current = {"delay":0*pq.ms, "duration": 0*pq.ms, "amplitude": 0*pq.nA}, stop_on_spike=False): # Set the units that NEURON uses current["delay"].units = pq.ms current["duration"].units = pq.ms current["amplitude"].units = pq.nA self.injector.delay = float(current["delay"]) self.injector.dur = float(current["duration"]) self.injector.amp = float(current["amplitude"]) if "sampling_period" in current: self.setup_recorders(current["sampling_period"]) if not stop_on_spike: self.h.run() else: self.h.stdinit() final_stop = self.h.tstop step = 10 while self.h.t < final_stop - 0.001: if self.h.t >= self.injector.delay - 0.001: next_stop = min(self.h.t + step, final_stop) else: next_stop = self.injector.delay self.h.runStopAt = next_stop self.h.continuerun(next_stop) self.h.stoprun = 1 voltage = self.get_membrane_potential() aps = get_zero_crossings_neg2pos(voltage, current["delay"]) if len(aps) > 0: self.reset_instruments() return voltage voltage = self.get_membrane_potential() self.reset_instruments() return voltage
[docs] def clamp_voltage(self, voltages=[0*pq.mV, 0*pq.mV, 0*pq.mV], durations=[0]*3*pq.ms): self.vclamp.amp1, self.vclamp.amp2, self.vclamp.amp3 = [float(v) for v in voltages] self.vclamp.dur1, self.vclamp.dur2, self.vclamp.dur3 = [float(d) for d in durations] self.h.run() current = self.nrn_vector_to_AnalogSignal(self.vciVector, pq.nA) self.reset_instruments() return current
[docs] def get_membrane_potential(self): return self.nrn_vector_to_AnalogSignal(self.vVector, pq.mV)
[docs] def nrn_vector_to_AnalogSignal(self, vector, units): ''' Resample the signal stored by the NEURON vector at the specified steps_per_ms frequency :param vector: reference to a NEURON h.Vector() :param units: the units to use with the result :param steps_per_ms: the number of points to use to represent each ms of the recorded signal :return: ''' #t = self.tVector.as_numpy() signal = vector.as_numpy() # new_t = np.linspace(t[0],t[-1],int(round(steps_per_ms*(t[-1]-t[0])))) # new_sig = np.interp(new_t, t, signal) return AnalogSignal(signal, sampling_period=self.sampling_period * pq.ms, units=units)
def __hash__(self): hash_tuple = ( self.name, self.h.dt if self.h.cvode_active() == 0 else 0, self.default_sampling_period, str(self.in_seg), str(self.out_seg) ) result = hash(hash_tuple) return result