def setUp(self): #swc_file = os.path.join(os.path.dirname(__file__), "validation", "ballanddoublestick", "double.swc") swc_file = os.path.join(os.path.dirname(__file__), "validation", "striatum", "fs", "str-fs-e161205_FS1-mMTC180800A-IDB-v20190312", "MTC180800A-IDB-cor-rep.swc") self.nm = NeuronMorphology(swc_filename=swc_file, load_morphology=True)
def read_prototypes(self): for name, definition in self.config["Neurons"].items(): morph = definition["morphology"] self.prototype_neurons[name] = NeuronMorphology(name=name, swc_filename=morph) # TODO: The code below is duplicate from detect.py, update so both use same code base for name, definition in self.config["Connectivity"].items(): pre_type, post_type = name.split(",") con_def = definition.copy() for key in con_def: if key == "GapJunction": con_def[key]["channelModelID"] = 3 else: con_def[key]["channelModelID"] = self.next_channel_model_id self.next_channel_model_id += 1 # Also if conductance is just a number, add std 0 if type(con_def[key]["conductance"]) not in [list, tuple]: con_def[key]["conductance"] = [ con_def[key]["conductance"], 0 ] self.connectivity_distributions[pre_type, post_type] = con_def
def load_neuron(self, neuron_id): neuron_info = self.data["neurons"][neuron_id] self.load_config_file() prototype_info = self.config["Neurons"][neuron_info["name"]] from snudda.neurons.neuron_morphology import NeuronMorphology neuron = NeuronMorphology(name=neuron_info["name"], position=neuron_info["position"], rotation=neuron_info["rotation"], swc_filename=prototype_info["morphology"], mech_filename=prototype_info["mechanisms"], load_morphology=True) return neuron
def load_neuron(self, neuron_info=None, neuron_id=None): assert (neuron_info is None) + ( neuron_id is None) == 1, "Specify neuron_info or neuron_id" if neuron_id is not None: print(f"Using id {neuron_id}") neuron_info = self.sl.data["neurons"][neuron_id] neuron_name = neuron_info["name"] if neuron_name not in self.prototype_neurons: self.prototype_neurons[neuron_name] = NeuronMorphology( name=neuron_name, swc_filename=neuron_info["morphology"]) neuron = self.prototype_neurons[neuron_name].clone() neuron.place(rotation=neuron_info["rotation"], position=neuron_info["position"]) return neuron
def get_rotations(self, volume_name, neuron_type, neuron_positions, rng): if (volume_name, neuron_type) in self.rotation_lookup: rotation_mode, field_position, field_rotation = self.rotation_lookup[volume_name, neuron_type] else: rotation_mode, field_position, field_rotation = "random", None, None if not rotation_mode or rotation_mode.lower() == "none": rotation_matrices = [np.eye]*neuron_positions.shape[0] elif rotation_mode in ["random", "default"]: rotation_matrices = [NeuronMorphology.rand_rotation_matrix(rand_nums=rng.random(size=(3,))) for x in range(0, neuron_positions.shape[0])] elif "vectorField" in rotation_mode: rotation_vectors = griddata(points=field_position, values=field_rotation, xi=neuron_positions, method="linear") assert not np.isnan(np.sum(rotation_vectors)), \ (f"Invalid rotation vector for volume {volume_name}, neuron {neuron_type}, " f"is neuron position outside the field?\nNeuron positions: {neuron_positions}" f" (must be inside convex hull of the field's positions points)") if rotation_mode == "vectorFieldAndZ": rotation_matrices = [np.matmul(SnuddaRotate.rotation_matrix_from_vectors(np.array([0, 0, 1]), rv), self.random_z_rotate(rng)) for rv in rotation_vectors] else: # We need to rotate z-axis to point to rotation_vector rotation_matrices = [SnuddaRotate.rotation_matrix_from_vectors(np.array([0, 0, 1]), rv) for rv in rotation_vectors] else: raise TypeError(f"Unknown rotation mode {rotation_mode}") return rotation_matrices
def add_neurons(self, swc_filename, num_neurons, param_data=None, mech_filename=None, modulation=None, name="Unnamed", hoc=None, volume_id=None, rotation_mode="random", virtual_neuron=False, axon_density=None): assert volume_id is not None, "You must specify a volume for neuron " + name nm = NeuronMorphology(swc_filename=swc_filename, param_data=param_data, mech_filename=mech_filename, name=name, hoc=hoc, virtual_neuron=virtual_neuron) neuron_type = name.split("_")[0] neuron_positions = self.volume[volume_id]["mesh"].place_neurons( num_neurons, neuron_type) first_added = True neuron_rotations = self.rotate_helper.get_rotations( volume_name=volume_id, neuron_type=neuron_type, neuron_positions=neuron_positions, rng=self.random_generator) for coords, rotation in zip(neuron_positions, neuron_rotations): # We set loadMorphology = False, to preserve memory # Only morphology loaded for nm then, to get axon and dend # radius needed for connectivity # Pick a random parameterset # parameter.json can be a list of lists, this allows you to select the # parameterset randomly # modulation.json is similarly formatted, pick a parameter set here parameter_id = self.random_generator.integers(1000000) modulation_id = self.random_generator.integers(1000000) n = nm.clone(position=coords, rotation=rotation, load_morphology=False, parameter_id=parameter_id, modulation_id=modulation_id) # self.writeLog("Place " + str(self.cellPos[i,:])) n.neuron_id = len(self.neurons) n.volume_id = volume_id assert axon_density is None or len(n.axon) == 0, \ "!!! ERROR: Neuron: " + str(n.name) + " has both axon and axon density." n.axon_density = axon_density self.neurons.append(n) # This info is used by workers to speed things up if first_added: first_added = False self.neuronPrototypes[n.name] = n
def __init__( self, neuron_morphology, neuron_mechanisms, neuron_parameters, neuron_modulation, stim_times, synapse_density, num_synapses, synapse_section_id=None, # if given, nSynapses is ignored synapse_section_x=None, # same # of elements as synapseSectionID neuron_parameter_id=0, # Which param set in parameter file to use neuron_modulation_id=0, holding_voltage=-70e-3, holding_current=None, synapse_type='glut', params={}, time=2, random_seed=None, log_file=None, verbose=True): self.log_file = log_file # File pointer self.verbose = verbose self.rng = np.random.default_rng(random_seed) self.write_log("Holding voltage: " + str(holding_voltage) + " V") self.write_log("Stim times: " + str(stim_times) + " s") self.write_log("Synapse type: " + str(synapse_type)) self.time = time self.synapses = [] self.i_clamp = None self.nc_syn = None self.i_save = None self.t_save = None self.v_save = None self.stim_vector = None self.vec_stim = None self.synapse_section_id = None self.synapse_section_x = None # Done in NrnSimulatorParallel # neuron.h.load_file('stdrun.hoc') self.sim = NrnSimulatorParallel(cvode_active=False) # Should we use weak reference for garbage collection? (weakref package) # We load the neuron morphology object also, used to place synapses self.write_log("Using morphology: " + str(neuron_morphology)) self.morphology = NeuronMorphology(swc_filename=neuron_morphology) # We need to setup the Neuron model self.neuron = NeuronModel(param_file=neuron_parameters, morph_file=neuron_morphology, mech_file=neuron_mechanisms, cell_name="OptimisationNeuron", modulation_file=neuron_modulation, parameter_id=neuron_parameter_id, modulation_id=neuron_modulation_id) self.neuron.instantiate(sim=self.sim) self.set_resting_voltage(holding_voltage * 1e3) neuron.h.celsius = 35 # gnabar_hh: The maximum specific sodium channel conductance [Default value = 0.120 S/cm2] # gkbar_hh: The maximum specific potassium channel conductance [Default value = 0.036 S/cm2] # gl_hh: The maximum specific leakage conductance [Default value = 0.0003 S/cm2] # ena: The reversal potential for the sodium channel [Default value = 50 mV] # ek: The reversal potential for the potassium channel [Default value = -77 mV] # el_hh: The reversal potential for the leakage channel [Default value = -54.3 mV] # We need to set the params also self.params = params self.default_cond = 5e-7 # This returns (section,sectionX) so we can reuse it if needed self.synapse_positions = self.add_synapse_density( synapse_type=synapse_type, synapse_density=synapse_density, num_synapses=num_synapses, section_id=synapse_section_id, section_x=synapse_section_x) self.stim_times = np.array(stim_times) # Assumes input in seconds (SI units) self.connect_input_to_synapses(self.stim_times) self.soma_record() self.synapse_current_record() self.holding_current = self.update_holding_current( holding_voltage=holding_voltage, holding_current=holding_current)
class RunSynapseRun(object): def __init__( self, neuron_morphology, neuron_mechanisms, neuron_parameters, neuron_modulation, stim_times, synapse_density, num_synapses, synapse_section_id=None, # if given, nSynapses is ignored synapse_section_x=None, # same # of elements as synapseSectionID neuron_parameter_id=0, # Which param set in parameter file to use neuron_modulation_id=0, holding_voltage=-70e-3, holding_current=None, synapse_type='glut', params={}, time=2, random_seed=None, log_file=None, verbose=True): self.log_file = log_file # File pointer self.verbose = verbose self.rng = np.random.default_rng(random_seed) self.write_log("Holding voltage: " + str(holding_voltage) + " V") self.write_log("Stim times: " + str(stim_times) + " s") self.write_log("Synapse type: " + str(synapse_type)) self.time = time self.synapses = [] self.i_clamp = None self.nc_syn = None self.i_save = None self.t_save = None self.v_save = None self.stim_vector = None self.vec_stim = None self.synapse_section_id = None self.synapse_section_x = None # Done in NrnSimulatorParallel # neuron.h.load_file('stdrun.hoc') self.sim = NrnSimulatorParallel(cvode_active=False) # Should we use weak reference for garbage collection? (weakref package) # We load the neuron morphology object also, used to place synapses self.write_log("Using morphology: " + str(neuron_morphology)) self.morphology = NeuronMorphology(swc_filename=neuron_morphology) # We need to setup the Neuron model self.neuron = NeuronModel(param_file=neuron_parameters, morph_file=neuron_morphology, mech_file=neuron_mechanisms, cell_name="OptimisationNeuron", modulation_file=neuron_modulation, parameter_id=neuron_parameter_id, modulation_id=neuron_modulation_id) self.neuron.instantiate(sim=self.sim) self.set_resting_voltage(holding_voltage * 1e3) neuron.h.celsius = 35 # gnabar_hh: The maximum specific sodium channel conductance [Default value = 0.120 S/cm2] # gkbar_hh: The maximum specific potassium channel conductance [Default value = 0.036 S/cm2] # gl_hh: The maximum specific leakage conductance [Default value = 0.0003 S/cm2] # ena: The reversal potential for the sodium channel [Default value = 50 mV] # ek: The reversal potential for the potassium channel [Default value = -77 mV] # el_hh: The reversal potential for the leakage channel [Default value = -54.3 mV] # We need to set the params also self.params = params self.default_cond = 5e-7 # This returns (section,sectionX) so we can reuse it if needed self.synapse_positions = self.add_synapse_density( synapse_type=synapse_type, synapse_density=synapse_density, num_synapses=num_synapses, section_id=synapse_section_id, section_x=synapse_section_x) self.stim_times = np.array(stim_times) # Assumes input in seconds (SI units) self.connect_input_to_synapses(self.stim_times) self.soma_record() self.synapse_current_record() self.holding_current = self.update_holding_current( holding_voltage=holding_voltage, holding_current=holding_current) # import pdb # pdb.set_trace() ############################################################################ def __del__(self): # This should not be needed but... self.neuron = None self.morphology = None self.i_clamp = None self.v_clamp = None self.v_save = None self.t_save = None self.i_save = None self.nc_syn = None self.vec_stim = None self.stim_vector = None self.little_synapse = None ############################################################################ def update_holding_current(self, holding_voltage=None, holding_current=None): if holding_voltage is None: holding_voltage = self.holding_voltage else: self.holding_voltage = holding_voltage if holding_current is not None: if self.i_clamp is None: self.i_clamp = neuron.h.IClamp(self.neuron.icell.soma[0](0.5)) # Update current on iClamp self.i_clamp.amp = holding_current * 1e9 # Convert SI -> nA for NEURON self.i_clamp.dur = 2 * self.time * 1e3 self.set_resting_voltage(self.holding_voltage * 1e3) self.write_log( f"Set holding current {holding_current}A and holding voltage {holding_voltage}V" ) return holding_current self.write_log("Updating holding current, might take a bit of time") # Disable old iClamp temporarily if self.i_clamp is not None: self.i_clamp.amp = 0 # Setup a temporary VClamp self.v_clamp = neuron.h.SEClamp(self.neuron.icell.soma[0](0.5)) self.v_clamp.rs = 1e-9 self.v_clamp.amp1 = holding_voltage * 1e3 self.v_clamp.dur1 = self.time * 2 * 1e3 # self.writeLog("VClamp duration: " + str(self.VClamp.dur1)) neuron.h.finitialize(self.holding_voltage * 1e3) # !!! There is a WEIRD neuron bug, that if this tstop here is # different from duration of simulation, then the *SECOND* time # a model is initialised we get the length of tSave set by this # value, and not by the tStop of that simulation --- go figure! self.set_resting_voltage(self.holding_voltage * 1e3) neuron.h.tstop = self.time * 1e3 # Must set tstop neuron.h.run() if False: import matplotlib.pyplot as plt plt.figure() plt.plot(self.t_save, self.v_save) plt.title("Holding voltage should be " + str(self.holding_voltage * 1e3) + "mV") plt.xlabel("time (ms)") plt.ylabel("volt (mV)") plt.ion() plt.show() import pdb pdb.set_trace() cur = float(self.v_clamp.i) # Remove VClamp self.v_clamp = None if self.i_clamp is None: self.i_clamp = neuron.h.IClamp(self.neuron.icell.soma[0](0.5)) # Update current on iClamp self.i_clamp.amp = cur self.i_clamp.dur = 2 * self.time * 1e3 self.set_resting_voltage(self.holding_voltage * 1e3) self.write_log( f"Holding voltage {self.holding_voltage * 1e3} mV, IClamp amp = {cur} nA" ) return cur * 1e-9 # Convert to SI units ############################################################################ def set_stim_times(self, stim_times): if len(stim_times) != len(self.stim_times) or (stim_times != self.stim_times).any(): print(f"Setting stim times to {stim_times} s") self.write_log(f"Setting stim times to {stim_times} s") self.stim_vector = neuron.h.Vector(stim_times * 1e3) self.stim_times = stim_times * 1e3 ############################################################################ def add_synapse_density(self, synapse_type, synapse_density, num_synapses=None, plot_flag=False, section_id=None, section_x=None): if plot_flag: assert section_id is None and section_x is None, \ "Can not plot if sectionID and sectionX are given" input_coords, section_id, section_x, density_function, dist_syn_soma = \ self.morphology.dendrite_input_locations(synapse_density=synapse_density, num_locations=num_synapses, return_density=True) self.synapse_locations = input_coords dist_from_soma = self.morphology.dend[:, 4] # plot density function plt.figure() plt.plot(dist_from_soma * 1e6, density_function, 'o') plt.xlabel('distance from soma $(\mu m)$') plt.title('density function') plt.ion() plt.show() # plot histogram - distance synapses from soma plt.figure() plt.hist(dist_syn_soma * 1e6, edgecolor='gray', bins=dist_syn_soma.size) plt.xlabel('distance from soma $(\mu m)$') plt.ylabel('frequency') plt.title('synaptic distance from soma') plt.ion() plt.show() elif section_id is None or section_x is None: input_coords, section_id, section_x, dist_syn_soma = \ self.morphology.dendrite_input_locations(synapse_density=synapse_density, num_locations=num_synapses, rng=self.rng) self.synapse_locations = input_coords else: self.synapse_locations = "Unknown, you specified sectionX and sectionID, " \ + "but not synapse xyz coordinates." # This is so we can find out where the synapses were placed self.synapse_section_id = section_id self.synapse_section_x = section_x sections = self.neuron.map_id_to_compartment(section_id) for s, sx in zip(sections, section_x): self.add_synapse(synapse_type, s, sx, self.params) # Return synapse position if we want to reuse them elsewhere return sections, section_x ############################################################################ def add_synapse(self, synapse_type, section, section_x, params): section_x = np.maximum(section_x, 1e-6) # Cant be 0 or 1 try: if synapse_type.lower() == 'glut': syn = neuron.h.tmGlut_double(section(section_x)) elif synapse_type.lower() == "gaba": syn = neuron.h.tmGabaA(section(section_x)) else: assert False, f"Unknown synapse type: {synapse_type}" self.synapses.append(syn) except: import traceback tstr = traceback.format_exc() self.write_log(tstr) self.write_log( "Did you remember to run nrnivmodl first, to generate channels mod files?" ) exit(-1) for p in params: # Conductance is set separately, it is a property of netcon if p == "cond": self.default_cond = params["cond"] continue val = self.si_to_natural_units(p, params[p]) setattr(syn, p, val) self.write_log( f"Setting parameters: {p} = {val} (neuron natural units)") ############################################################################ def connect_input_to_synapses(self, stim_times): self.write_log(f"Stimulation times (s): {stim_times}") self.nc_syn = [] self.stim_vector = neuron.h.Vector(stim_times * 1e3) self.vec_stim = neuron.h.VecStim() self.vec_stim.play(self.stim_vector) for syn in self.synapses: ncs = neuron.h.NetCon(self.vec_stim, syn) ncs.delay = 0 ncs.threshold = 0 self.nc_syn.append(ncs) ############################################################################ def soma_record(self): self.t_save = neuron.h.Vector() self.t_save.record(neuron.h._ref_t) self.v_save = neuron.h.Vector() self.v_save.record(self.neuron.icell.soma[0](0.5)._ref_v) ############################################################################ def synapse_current_record(self): self.i_save = [] for syn in self.synapses: i_rec = neuron.h.Vector() i_rec.record(syn._ref_i) self.i_save.append(i_rec) ############################################################################ def run(self, tau, tau_r, tau_f, u, cond=None, time=None): assert False, "This is the old run method" if time is None: time = self.time else: self.time = time if cond is None: cond = self.default_cond # print(vars()) # print("Running: tau: %.3g, tauR: %.3g, tauF: %.3g, U: %.3g, cond: %.3g\n" \ # % (tau,tauR,tauF,U,cond)) # Convert from SI units to natural units that Neuron uses for ncs in self.nc_syn: ncs.weight[0] = 1 * cond * 1e6 for syn in self.synapses: syn.tau = tau * 1e3 syn.tau_r = tau_r * 1e3 syn.tau_f = tau_f * 1e3 syn.u = u # print(self.littleSynapse.tau) # print("Initialise voltage to " + str(self.holdingVoltage*1e3) + " mV") neuron.h.finitialize(self.holding_voltage * 1e3) # OLD : -70 neuron.h.tstop = time * 1e3 neuron.h.run() # self.tSave.resize() # self.vSave.resize() # self.iSave.resize() if np.array(self.t_save).shape != np.array(self.v_save).shape: self.write_log("THIS IS WRONG, should be same shape!!") self.write_log(f"size t = {np.array(self.t_save).shape}") self.write_log(f"size v = {np.array(self.v_save).shape}") import pdb pdb.set_trace() # print("Last V = " + str(self.vSave[-1]*1e-3)) # Convert back to SI units return (np.array(self.t_save) * 1e-3, np.array(self.v_save) * 1e-3, np.array(self.i_save) * 1e-9) ############################################################################ def set_resting_voltage(self, rest_volt): self.write_log("Setting resting voltage to %.3f mV" % rest_volt) soma = [x for x in self.neuron.icell.soma] axon = [x for x in self.neuron.icell.axon] dend = [x for x in self.neuron.icell.dend] cell = soma + axon + dend for sec in cell: for seg in sec.allseg(): seg.v = rest_volt ############################################################################ # I wish Neuron would use natural units... def si_to_natural_units(self, var_name, value): conv_factor = { "U": 1.0, "tauR": 1e3, "tauF": 1e3, "cond": 1e6, "tau": 1e3, "nmda_ratio": 1.0, "tau1_ampa": 1.0, # Ilaria's file has ms already "tau2_ampa": 1.0, # Ilaria's file has ms already "tau3_ampa": 1.0, # Ilaria's file has ms already "tau1_nmda": 1.0, # Ilaria's file has ms already "tau2_nmda": 1.0, # Ilaria's file has ms already "tau3_nmda": 1.0, # Ilaria's file has ms already "tpeak_ampa": 1.0, # Ilaria's file has ms already "tpeak_nmda": 1.0, # Ilaria's file has ms already :/ "ratio_nmda": 1.0, "I2_ampa": 1.0, # Ilaria's file has ms already "I3_ampa": 1.0, # Ilaria's file has ms already "I2_nmda": 1.0, # Ilaria's file has ms already "I3_nmda": 1.0, # Ilaria's file has ms already "factor_ampa": 1.0, # Ilaria's file has ms already "factor_nmda": 1.0 # Ilaria's file has ms already } if var_name not in conv_factor: self.write_log("Missing conversion fractor for " + str(var_name) \ + ". Please update SItoNaturalUnits function.") self.write_log("convFactor = " + str(conv_factor)) import pdb pdb.set_trace() try: return value * conv_factor[var_name] except: import traceback tstr = traceback.format_exc() self.write_log(tstr) import pdb pdb.set_trace() ############################################################################ def plot(self): ax = self.morphology.plot_neuron(axon_colour='red', dend_colour='blue', soma_colour='green') ax.scatter(self.synapse_locations[:, 0], self.synapse_locations[:, 1], self.synapse_locations[:, 2], color='black', marker='o', s=20) plt.ion() ############################################################################ # pars = { "tau1" : 0.25e-3 } # The code converts to natural units internally, if your parameter is missing # then update SItoNaturalUnits # OBS, soma parameters are ignored by run2 (they should be set during setup) def run2(self, pars, time=None, cond=1e-8): self.write_log(f"Running with pars: {pars}") if time is None: time = self.time else: self.time = time for p in pars: if p == "cond": cond = self.si_to_natural_units(p, pars[p]) else: v = self.si_to_natural_units(p, pars[p]) for s in self.synapses: setattr(s, p, v) neuron.h.finitialize(self.holding_voltage * 1e3) for ncs in self.nc_syn: ncs.weight[0] = cond self.set_resting_voltage(self.holding_voltage * 1e3) neuron.h.v_init = self.holding_voltage * 1e3 neuron.h.tstop = time * 1e3 self.write_log("About to start NEURON... stay safe") neuron.h.run() self.write_log("NEURON actually completed?!") # Convert results back to SI units return (np.array(self.t_save) * 1e-3, np.array(self.v_save) * 1e-3, np.array(self.i_save) * 1e-9) ############################################################################ def write_log(self, text, flush=True): # Change flush to False in future, debug if self.log_file is not None: self.log_file.write(text + "\n") if self.verbose: print(text) if flush: self.log_file.flush() else: if self.verbose: print(text)
def has_axon(swc_file): nm = NeuronMorphology(swc_filename=snudda_parse_path(swc_file)) return len(nm.axon) > 0
class MyTestCase(unittest.TestCase): def setUp(self): #swc_file = os.path.join(os.path.dirname(__file__), "validation", "ballanddoublestick", "double.swc") swc_file = os.path.join(os.path.dirname(__file__), "validation", "striatum", "fs", "str-fs-e161205_FS1-mMTC180800A-IDB-v20190312", "MTC180800A-IDB-cor-rep.swc") self.nm = NeuronMorphology(swc_filename=swc_file, load_morphology=True) def test_input_location(self, stage="dist_to_soma"): synapse_density = "(d > 100e-6)*1" rng = np.random.default_rng(123456) # xyz, sec_id, sec_x, dist_to_soma = self.nm.dendrite_input_locations(synapse_density=synapse_density, rng=rng, num_locations=100) # Please note that num_locations currently does not guarantee 100 synpases when requesting it # 3e-6 due to compartment length sampled at 3 micrometers self.assertTrue((dist_to_soma > 100e-6 - 3e-6).all()) self.assertEqual(xyz.shape[1], 3) self.assertEqual(len(dist_to_soma), xyz.shape[0]) self.assertEqual(len(dist_to_soma), len(sec_id)) self.assertEqual(len(sec_id), len(sec_x)) # Repeat test but for smaller than 100 synapse_density = "(d < 200e-6)*1" xyz, sec_id, sec_x, dist_to_soma = self.nm.dendrite_input_locations(synapse_density=synapse_density, rng=rng, num_locations=100) # 3e-6 due to compartment length sampled at 3 micrometers self.assertTrue((dist_to_soma < 200e-6 + 3e-6).all()) def test_rand_rotation(self, stage="rand_rotation"): for idx in range(0, 100): rot_mat = self.nm.rand_rotation_matrix() self.assertAlmostEqual(np.linalg.det(rot_mat), 1, places=10) def test_clone(self, stage="clone"): new_nm = self.nm.clone() self.assertTrue((self.nm.dend == new_nm.dend).all()) self.assertTrue((self.nm.axon == new_nm.axon).all()) self.assertTrue((self.nm.soma == new_nm.soma).all()) # Make sure that clone is a copy, and don't point back to same arrays ang = np.pi R_x = np.array([[1, 0, 0], [0, np.cos(ang), -np.sin(ang)], [0, np.sin(ang), np.cos(ang)]]) new_nm.place(rotation=R_x, position=np.array([0, 0, 0])) self.assertTrue((np.abs(self.nm.dend[:, 0] - new_nm.dend[:, 0]) < 1e-6).all()) self.assertTrue((np.abs(self.nm.dend[:, 1] + new_nm.dend[:, 1]) < 1e-6).all()) self.assertTrue((np.abs(self.nm.dend[:, 2] + new_nm.dend[:, 2]) < 1e-6).all()) self.assertTrue((np.abs(self.nm.axon[:, 0] - new_nm.axon[:, 0]) < 1e-6).all()) self.assertTrue((np.abs(self.nm.axon[:, 1] + new_nm.axon[:, 1]) < 1e-6).all()) self.assertTrue((np.abs(self.nm.axon[:, 2] + new_nm.axon[:, 2]) < 1e-6).all()) new_nm.place(position=np.array([1, 2, 3])) self.assertTrue((np.abs(new_nm.soma[0,:3] - np.array([1, 2, 3])) < 1e-6).all())
def __init__( self, neuronMorphology, neuronMechanisms, neuronParameters, neuronModulation, stimTimes, synapseDensity, nSynapses, synapseSectionID=None, # if given, nSynapses is ignored synapseSectionX=None, # same # of elements as synapseSectionID neuronParameterID=0, # Which param set in parameter file to use neuronModulationID=0, holdingVoltage=-70e-3, synapseType='glut', params={}, time=2, logFile=None, verbose=True, rng=None): self.logFile = logFile # File pointer self.verbose = verbose self.writeLog("Holding voltage: " + str(holdingVoltage) + " V") self.writeLog("Stim times: " + str(stimTimes) + " s") self.writeLog("Synapse type: " + str(synapseType)) self.time = time self.synapses = [] self.IClamp = None self.ncSyn = None # Done in NrnSimulatorParallel # neuron.h.load_file('stdrun.hoc') self.sim = NrnSimulatorParallel(cvode_active=False) # Should we use weak reference for garbage collection? (weakref package) # We load the neuron morphology object also, used to place synapses self.writeLog("Using morphology: " + str(neuronMorphology)) self.morphology = NeuronMorphology(swc_filename=neuronMorphology) # We need to setup the Neuron model self.neuron = NeuronModel(param_file=neuronParameters, morph_file=neuronMorphology, mech_file=neuronMechanisms, cell_name="OptimisationNeuron", modulation_file=neuronModulation, parameter_id=neuronParameterID, modulation_id=neuronModulationID) self.neuron.instantiate(sim=self.sim) self.setRestingVoltage(holdingVoltage * 1e3) neuron.h.celsius = 35 # gnabar_hh: The maximum specific sodium channel conductance [Default value = 0.120 S/cm2] # gkbar_hh: The maximum specific potassium channel conductance [Default value = 0.036 S/cm2] # gl_hh: The maximum specific leakage conductance [Default value = 0.0003 S/cm2] # ena: The reversal potential for the sodium channel [Default value = 50 mV] # ek: The reversal potential for the potassium channel [Default value = -77 mV] # el_hh: The reversal potential for the leakage channel [Default value = -54.3 mV] # We need to set the params also self.params = params self.defaultCond = 5e-7 self.rng = rng assert rng, " Code has been updated, rng must be a random number generator, eg. rng = np.random.default_rng(random_seed)" self.addSynapseDensity(synapseType, synapseDensity, nSynapses, synapseSectionID, synapseSectionX, rng=self.rng) self.stimTimes = stimTimes * 1e3 # Assumes input in seconds (SI units) self.connectInputToSynapses(stimTimes) self.somaRecord() self.synapseCurrentRecord() self.updateHoldingCurrent(holdingVoltage)