def __init__(self, **kwargs): """ :Parameters: kwargs : dict Keyword arguments, see Keywords. :Keywords: debug : bool Debug output toggle. Default=False externals : list A list of SimExternalDelegate instances. Internal state changes will be propagated to the delegates. Default=False sample_rate : float Sample rate. frame : int Frame to start at. frame_size : int Frame size. cfg : str Path to a config file, readable by a ConfigParser instance. """ # private property members self._cfg = None self._externals = [] self._frame = None self._frame_size = None self._sample_rate = None self._status = None # public members self.cls_dyn = ClusterDynamics() self.io_man = SimIOManager() self.neuron_data = NeuronDataContainer() self.debug = kwargs.get('debug', False) # externals for ext in kwargs.get('externals', []): if not isinstance(ext, SimExternalDelegate): continue self._externals.append(ext)
def __init__(self, **kwargs): """ :Parameters: kwargs : dict Keyword arguments, see Keywords. :Keywords: debug : bool Debug output toggle. Default=False externals : list A list of SimExternalDelegate instances. Internal state changes will be propagated to the delegates. Default=False sample_rate : float Sample rate. frame : int Frame to start at. frame_size : int Frame size. cfg : str Path to a config file, readable by a ConfigParser instance. """ # private property members self._cfg = None self._externals = [] self._frame = None self._frame_size = None self._sample_rate = None self._status = None # public members self.cls_dyn = ClusterDynamics() self.io_man = SimIOManager() self.neuron_data = NeuronDataContainer() self.debug = kwargs.get("debug", False) # externals for ext in kwargs.get("externals", []): if not isinstance(ext, SimExternalDelegate): continue self._externals.append(ext)
class BaseSimulation(dict): """simulation class controlling the scene""" ## constructor def __init__(self, **kwargs): """ :Parameters: kwargs : dict Keyword arguments, see Keywords. :Keywords: debug : bool Debug output toggle. Default=False externals : list A list of SimExternalDelegate instances. Internal state changes will be propagated to the delegates. Default=False sample_rate : float Sample rate. frame : int Frame to start at. frame_size : int Frame size. cfg : str Path to a config file, readable by a ConfigParser instance. """ # private property members self._cfg = None self._externals = [] self._frame = None self._frame_size = None self._sample_rate = None self._status = None # public members self.cls_dyn = ClusterDynamics() self.io_man = SimIOManager() self.neuron_data = NeuronDataContainer() self.debug = kwargs.get("debug", False) # externals for ext in kwargs.get("externals", []): if not isinstance(ext, SimExternalDelegate): continue self._externals.append(ext) def initialize(self, **kwargs): """initialize the simulation :Keywords: debug : bool Debug flag, enables verbose output. Default=False frame : long Frame id to start at. Default=0 frame_size : int The frame size. Default=16 sample_rate : float Sample rate to operate. Default=16000.0 """ self.clear() # reset private members self.sample_rate = kwargs.get("sample_rate", 16000.0) self.frame = kwargs.get("frame", 0) self.frame_size = kwargs.get("frame_size", 1024) self.status # reset pubic members self.cls_dyn.clear() self.io_man.initialize() self.neuron_data.clear() def finalize(self): """finalize the simulation""" self.clear() # reset pubic members self.cls_dyn.clear() self.io_man.finalize() self.neuron_data.clear() ## properties def get_frame(self): return self._frame def set_frame(self, value): self._frame = long(value) for ext in self._externals: ext.frame(self._frame) frame = property(get_frame, set_frame) def get_frame_size(self): return self._frame_size def set_frame_size(self, value): self._frame_size = int(value) for ext in self._externals: ext.frame_size(self._frame_size) self.status frame_size = property(get_frame_size, set_frame_size) def get_sample_rate(self): return self._sample_rate def set_sample_rate(self, value): self._sample_rate = float(value) self.cls_dyn.sample_rate = self._sample_rate for ext in self._externals: ext.sample_rate(self._sample_rate) self.status sample_rate = property(get_sample_rate, set_sample_rate) def get_status(self): self._status = { "frame_size": self.frame_size, "sample_rate": self.sample_rate, "neurons": self.neuron_keys, "recorders": self.recorder_keys, } if self.io_man.is_initialized: self.io_man.status = self._status return self._status status = property(get_status) def get_neuron_keys(self): return [idx for idx in self if isinstance(self[idx], Neuron)] neuron_keys = property(get_neuron_keys) def get_recorder_keys(self): return [idx for idx in self if isinstance(self[idx], Recorder)] recorder_keys = property(get_recorder_keys) ## simulation control methods def simulate(self): """advance the simulation by one frame""" # inc frame counter self.frame += 1 # process events self._simulate_io_tick() # process units self._simulate_neuron_tick() # record for recorders self._simulate_recorder_tick() def _simulate_io_tick(self): """process io loop for the current frame This will tick the SimIOManager and process all queued events. """ # get events events = self.io_man.tick() while len(events) > 0: pkg = events.pop(0) print pkg log_str = ">>> " if pkg.ident in self: # recorder event if isinstance(self[pkg.ident], Recorder): log_str += "R[%s]:" % pkg.ident # position event if pkg.tid == SimPkg.T_POS: # position request if pkg.nitems == 0: log_str += "MOVE[%s] - request" % (self[pkg.ident].name) # reposition request elif pkg.nitems == 1: pos, vel = pkg.cont[0].cont[:2] log_str += "MOVE: %s, %s" % (pos, vel) self[pkg.ident].trajectory_pos = pos # TODO: implementation of the velocity component # weird position event else: print "weird event, was T_POS with:", pkg.cont continue # send position acknowledgement self.io_man.send_package(SimPkg.T_POS, pkg.ident, self._frame, self[pkg.ident].trajectory_pos) # neuron event elif isinstance(self[pkg.ident], Neuron): log_str += "N:[%s] neuron event" % pkg.ident else: log_str += "ANY:[%s] unknown" % pkg.ident # other event else: log_str += "O[%s]:" % pkg.ident if pkg.tid == SimPkg.T_CON: log_str += "CONNECT from %s" % str(pkg.cont) elif pkg.tid == SimPkg.T_END: log_str += "DISCONNECT from %s" % str(pkg.cont) else: log_str += "unknown" # log self.log(log_str) def _simulate_neuron_tick(self): """process neurons for current frame This will generate spike trains and configure the neuronal firing behavior for the current frame. """ # generate spike trains for the scene self.cls_dyn.generate(self.frame_size) # propagate spike trains to neurons for nrn_k in self.neuron_keys: self[nrn_k].simulate(frame_size=self.frame_size, firing_times=self.cls_dyn.get_spike_train(nrn_k)) def _simulate_recorder_tick(self): """process recorders for the current frame This will record waveforms and grountruth for the current frame. """ # list of all neurons nlist = [self[nrn_k] for nrn_k in self.neuron_keys] # record per recorder for rec_k in self.recorder_keys: self.io_man.send_package( SimPkg.T_REC, rec_k, self._frame, self[rec_k].simulate(nlist=nlist, frame_size=self.frame_size) ) ## methods logging def log(self, log_str): """log a string""" for ext in self._externals: ext.log("[#%09d] %s" % (self.frame, log_str)) def log_d(self, log_str): """log a string""" if self.debug is True: for ext in self._externals: ext.log("[DEBUG] %s" % log_str) ## methods object management def register_neuron(self, **kwargs): """register a neuron to the simulation :Keywords: neuron_data : NeuronData or path or None Reference to a NeuronData object or a path to the HDF5 archive where the data can be load from. CAUTION! str != QString etc.. use str(neuron_data)! position : arraylike Position in the scene (x,y,z). Default=[0,0,0] orientation : arraylike Orientation of the object. If ndarray it is interpreted as direction of orientation relative to the scene's positive z-axis. If its a list or tuple, it is interpreted as a triple of euler angles relative to the scene's positive z-axis. If it is True a random rotation is created. Default=False rate_of_fire : float Rate of fire in Hz. Default=50.0 amplitude : float Amplitude of the waveform. Default=1.0 cluster : int The cluster idx :Raises: some error .. mostly ValueErrors for invalid parameters. :Returns: The string representation of the registered Neuron. """ # check neuron data try: neuron_data = kwargs.pop("neuron_data") except: raise ValueError('No "neuron_data" given!') if neuron_data in self.neuron_data: ndata = self.neuron_data[neuron_data] else: if not self.neuron_data.insert(neuron_data): raise ValueError("Unknown neuron_data: %s" % str(neuron_data)) else: ndata = self.neuron_data[neuron_data] kwargs.update(neuron_data=ndata) # build neuron neuron = Neuron(**kwargs) self[id(neuron)] = neuron # register in cluster dynamics cls_idx = kwargs.get("cluster", None) if cls_idx is not None: cls_idx = int(cls_idx) self.cls_dyn.add_neuron(neuron, cls_idx=cls_idx) # log and return self.log(">> %s created!" % neuron) self.status return str(neuron) def register_recorder(self, **kwargs): """register a recorder for simulation :Keywords: position : array like Position in scene (x,y,z). Default=[0,0,0] orientation : arraylike or bool Orientation of the object. If ndarray it is interpreted as direction of orientation relative to the scene's positive z-axis. If its a list or tuple, it is interpreted as a triple of euler angles relative to the scene's positive z-axis. If it is True a random rotation is created. Default=False scale : float Scale factor for the Tetrahedron height. The default uses values from the Thomas Recording GmbH website, with a tetrode of about 40µm height and 20µm for the basis triangle. Note that negative values invert the tetrode direction with respect to self.position. Default=1.0 snr : float Signal to Noise Ratio (SNR) for the noise process. Default=1.0 TODO: fix this value to be congruent with the neuron amplitudes. noise_params : list The noise generator parameters. Default=None :Raises: some error ..mostly ValueError for invalid parameters. :Returns: The string representation of the registered Recorder. """ # build tetrode tetrode = Tetrode(**kwargs) self[id(tetrode)] = tetrode # connect and return self.log(">> %s created!" % tetrode) self.status return str(tetrode) def remove_object(self, key): """remove the SimObject with objectName 'key' :Parameters: key : SimObject or int/long A SimObject or the id of a SimObject attached (as int/long/str). :Returns: True on successful removal, False else. """ # convert the key if isinstance(key, SimObject): lookup = id(key) elif isinstance(key, (str, unicode, int, long)): try: lookup = long(key) except: try: lookup = long(key, 16) except: return False else: return False # remove item try: item = self.pop(lookup) self.log(">> %s destroyed!" % item) self.status return True except: return False def scene_config_load(self, fname): """load a scene configuration file :Parameters: fname : str Path to the scene configuration file. """ # checks and inits cfg = ConfigParser() cfg_check = cfg.read(fname) if fname not in cfg_check: raise IOError("could not load scene from %s" % fname) # check for config ndata_paths = cfg.get("CONFIG", "neuron_data_dir") ndata_paths = ndata_paths.strip().split("\n") # read per section for sec in cfg.sections(): # check section sec_str = sec.split() cls = sec_str[0] if cls not in ["Neuron", "Tetrode"]: continue kwargs = {} if len(sec_str) > 1: kwargs["name"] = " ".join(sec_str[1:]) # elaborate class and keyword arguments bad_ndata = False for k, v in cfg.items(sec): # read items if k is None or v is None or k == "" or v == "": continue elif k in ["position", "orientation", "trajectory"]: if v == "False": kwargs[k] = False elif v == "Truse": kwargs[k] = True else: kwargs[k] = map(float, v.split()) elif k in ["neuron_data"]: kwargs[k] = v ndata_path_list = [osp.join(path, v) for path in ndata_paths] added_ndata = self.neuron_data.insert(ndata_path_list) if added_ndata == 0: bad_ndata = True else: kwargs[k] = v # delegate action if bad_ndata: continue elif cls == "Neuron": self.register_neuron(**kwargs) elif cls == "Tetrode": self.register_recorder(**kwargs) def scene_config_save(self, fname): """save the current scene configuration to a file :Parameters: fname : str Path to save tha cfg to. """ # checks and inits cfg = ConfigParser() # write CONFIG section cfg.add_section("CONFIG") cfg.set("CONFIG", "frame_size", self.frame_size) cfg.set("CONFIG", "sample_rate", self.sample_rate) ndata_paths = "\t\n".join(self.neuron_data.paths) cfg.set("CONFIG", "neuron_data_dir", ndata_paths) # helper functions npy2cfg = lambda x: " ".join(map(str, x.tolist())) # add per SimObject for obj in self.values(): if isinstance(obj, Neuron): name = "Neuron %s" % obj.name cfg.add_section(name) cfg.set(name, "cluster", str(self.cls_dyn.get_cls_for_nrn(obj))) cfg.set(name, "position", npy2cfg(obj.position)) if obj.orientation is True or obj.orientation is False: cfg.set(name, "orientation", obj.orientation) else: cfg.set(name, "orientation", npy2cfg(obj.orientation)) cfg.set(name, "rate_of_fire", str(obj.rate_of_fire)) cfg.set(name, "amplitude", str(obj.amplitude)) cfg.set(name, "neuron_data", str(obj._neuron_data.filename)) elif isinstance(obj, Recorder): name = "Tetrode %s" % obj.name cfg.add_section(name) cfg.set(name, "position", npy2cfg(obj.position)) cfg.set(name, "orientation", npy2cfg(obj._trajectory)) cfg.set(name, "snr", str(obj.snr)) # save save_file = open(fname, "w") cfg.write(save_file) save_file.close() ## methods config loading def load_config(self, cfg_path=None): """loads initialization values from file :Parameters: cfg_file : str Path to the config file. Should be readable by a ConfigParser. """ # TODO: implement config file handling ## special methods def __len__(self): return len(filter(lambda x: isinstance(x, Neuron), self.values())) def __str__(self): return "BaseSimulation :: %d items" % len(self)
class BaseSimulation(dict): """simulation class controlling the scene""" ## constructor def __init__(self, **kwargs): """ :Parameters: kwargs : dict Keyword arguments, see Keywords. :Keywords: debug : bool Debug output toggle. Default=False externals : list A list of SimExternalDelegate instances. Internal state changes will be propagated to the delegates. Default=False sample_rate : float Sample rate. frame : int Frame to start at. frame_size : int Frame size. cfg : str Path to a config file, readable by a ConfigParser instance. """ # private property members self._cfg = None self._externals = [] self._frame = None self._frame_size = None self._sample_rate = None self._status = None # public members self.cls_dyn = ClusterDynamics() self.io_man = SimIOManager() self.neuron_data = NeuronDataContainer() self.debug = kwargs.get('debug', False) # externals for ext in kwargs.get('externals', []): if not isinstance(ext, SimExternalDelegate): continue self._externals.append(ext) def initialize(self, **kwargs): """initialize the simulation :Keywords: debug : bool Debug flag, enables verbose output. Default=False frame : long Frame id to start at. Default=0 frame_size : int The frame size. Default=16 sample_rate : float Sample rate to operate. Default=16000.0 """ self.clear() # reset private members self.sample_rate = kwargs.get('sample_rate', 16000.0) self.frame = kwargs.get('frame', 0) self.frame_size = kwargs.get('frame_size', 1024) self.status # reset pubic members self.cls_dyn.clear() self.io_man.initialize() self.neuron_data.clear() def finalize(self): """finalize the simulation""" self.clear() # reset pubic members self.cls_dyn.clear() self.io_man.finalize() self.neuron_data.clear() ## properties def get_frame(self): return self._frame def set_frame(self, value): self._frame = long(value) for ext in self._externals: ext.frame(self._frame) frame = property(get_frame, set_frame) def get_frame_size(self): return self._frame_size def set_frame_size(self, value): self._frame_size = int(value) for ext in self._externals: ext.frame_size(self._frame_size) self.status frame_size = property(get_frame_size, set_frame_size) def get_sample_rate(self): return self._sample_rate def set_sample_rate(self, value): self._sample_rate = float(value) self.cls_dyn.sample_rate = self._sample_rate for ext in self._externals: ext.sample_rate(self._sample_rate) self.status sample_rate = property(get_sample_rate, set_sample_rate) def get_status(self): self._status = { 'frame_size': self.frame_size, 'sample_rate': self.sample_rate, 'neurons': self.neuron_keys, 'recorders': self.recorder_keys, } if self.io_man.is_initialized: self.io_man.status = self._status return self._status status = property(get_status) def get_neuron_keys(self): return [idx for idx in self if isinstance(self[idx], Neuron)] neuron_keys = property(get_neuron_keys) def get_recorder_keys(self): return [idx for idx in self if isinstance(self[idx], Recorder)] recorder_keys = property(get_recorder_keys) ## simulation control methods def simulate(self): """advance the simulation by one frame""" # inc frame counter self.frame += 1 # process events self._simulate_io_tick() # process units self._simulate_neuron_tick() # record for recorders self._simulate_recorder_tick() def _simulate_io_tick(self): """process io loop for the current frame This will tick the SimIOManager and process all queued events. """ # get events events = self.io_man.tick() while len(events) > 0: pkg = events.pop(0) print pkg log_str = '>>> ' if pkg.ident in self: # recorder event if isinstance(self[pkg.ident], Recorder): log_str += 'R[%s]:' % pkg.ident # position event if pkg.tid == SimPkg.T_POS: # position request if pkg.nitems == 0: log_str += 'MOVE[%s] - request' % ( self[pkg.ident].name) # reposition request elif pkg.nitems == 1: pos, vel = pkg.cont[0].cont[:2] log_str += 'MOVE: %s, %s' % (pos, vel) self[pkg.ident].trajectory_pos = pos # TODO: implementation of the velocity component # weird position event else: print 'weird event, was T_POS with:', pkg.cont continue # send position acknowledgement self.io_man.send_package( SimPkg.T_POS, pkg.ident, self._frame, self[pkg.ident].trajectory_pos) # neuron event elif isinstance(self[pkg.ident], Neuron): log_str += 'N:[%s] neuron event' % pkg.ident else: log_str += 'ANY:[%s] unknown' % pkg.ident # other event else: log_str += 'O[%s]:' % pkg.ident if pkg.tid == SimPkg.T_CON: log_str += 'CONNECT from %s' % str(pkg.cont) elif pkg.tid == SimPkg.T_END: log_str += 'DISCONNECT from %s' % str(pkg.cont) else: log_str += 'unknown' # log self.log(log_str) def _simulate_neuron_tick(self): """process neurons for current frame This will generate spike trains and configure the neuronal firing behavior for the current frame. """ # generate spike trains for the scene self.cls_dyn.generate(self.frame_size) # propagate spike trains to neurons for nrn_k in self.neuron_keys: self[nrn_k].simulate( frame_size=self.frame_size, firing_times=self.cls_dyn.get_spike_train(nrn_k)) def _simulate_recorder_tick(self): """process recorders for the current frame This will record waveforms and grountruth for the current frame. """ # list of all neurons nlist = [self[nrn_k] for nrn_k in self.neuron_keys] # record per recorder for rec_k in self.recorder_keys: self.io_man.send_package( SimPkg.T_REC, rec_k, self._frame, self[rec_k].simulate(nlist=nlist, frame_size=self.frame_size)) ## methods logging def log(self, log_str): """log a string""" for ext in self._externals: ext.log('[#%09d] %s' % (self.frame, log_str)) def log_d(self, log_str): """log a string""" if self.debug is True: for ext in self._externals: ext.log('[DEBUG] %s' % log_str) ## methods object management def register_neuron(self, **kwargs): """register a neuron to the simulation :Keywords: neuron_data : NeuronData or path or None Reference to a NeuronData object or a path to the HDF5 archive where the data can be load from. CAUTION! str != QString etc.. use str(neuron_data)! position : arraylike Position in the scene (x,y,z). Default=[0,0,0] orientation : arraylike Orientation of the object. If ndarray it is interpreted as direction of orientation relative to the scene's positive z-axis. If its a list or tuple, it is interpreted as a triple of euler angles relative to the scene's positive z-axis. If it is True a random rotation is created. Default=False rate_of_fire : float Rate of fire in Hz. Default=50.0 amplitude : float Amplitude of the waveform. Default=1.0 cluster : int The cluster idx :Raises: some error .. mostly ValueErrors for invalid parameters. :Returns: The string representation of the registered Neuron. """ # check neuron data try: neuron_data = kwargs.pop('neuron_data') except: raise ValueError('No "neuron_data" given!') if neuron_data in self.neuron_data: ndata = self.neuron_data[neuron_data] else: if not self.neuron_data.insert(neuron_data): raise ValueError('Unknown neuron_data: %s' % str(neuron_data)) else: ndata = self.neuron_data[neuron_data] kwargs.update(neuron_data=ndata) # build neuron neuron = Neuron(**kwargs) self[id(neuron)] = neuron # register in cluster dynamics cls_idx = kwargs.get('cluster', None) if cls_idx is not None: cls_idx = int(cls_idx) self.cls_dyn.add_neuron(neuron, cls_idx=cls_idx) # log and return self.log('>> %s created!' % neuron) self.status return str(neuron) def register_recorder(self, **kwargs): """register a recorder for simulation :Keywords: position : array like Position in scene (x,y,z). Default=[0,0,0] orientation : arraylike or bool Orientation of the object. If ndarray it is interpreted as direction of orientation relative to the scene's positive z-axis. If its a list or tuple, it is interpreted as a triple of euler angles relative to the scene's positive z-axis. If it is True a random rotation is created. Default=False scale : float Scale factor for the Tetrahedron height. The default uses values from the Thomas Recording GmbH website, with a tetrode of about 40µm height and 20µm for the basis triangle. Note that negative values invert the tetrode direction with respect to self.position. Default=1.0 snr : float Signal to Noise Ratio (SNR) for the noise process. Default=1.0 TODO: fix this value to be congruent with the neuron amplitudes. noise_params : list The noise generator parameters. Default=None :Raises: some error ..mostly ValueError for invalid parameters. :Returns: The string representation of the registered Recorder. """ # build tetrode tetrode = Tetrode(**kwargs) self[id(tetrode)] = tetrode # connect and return self.log('>> %s created!' % tetrode) self.status return str(tetrode) def remove_object(self, key): """remove the SimObject with objectName 'key' :Parameters: key : SimObject or int/long A SimObject or the id of a SimObject attached (as int/long/str). :Returns: True on successful removal, False else. """ # convert the key if isinstance(key, SimObject): lookup = id(key) elif isinstance(key, (str, unicode, int, long)): try: lookup = long(key) except: try: lookup = long(key, 16) except: return False else: return False # remove item try: item = self.pop(lookup) self.log('>> %s destroyed!' % item) self.status return True except: return False def scene_config_load(self, fname): """load a scene configuration file :Parameters: fname : str Path to the scene configuration file. """ # checks and inits cfg = ConfigParser() cfg_check = cfg.read(fname) if fname not in cfg_check: raise IOError('could not load scene from %s' % fname) # check for config ndata_paths = cfg.get('CONFIG', 'neuron_data_dir') ndata_paths = ndata_paths.strip().split('\n') # read per section for sec in cfg.sections(): # check section sec_str = sec.split() cls = sec_str[0] if cls not in ['Neuron', 'Tetrode']: continue kwargs = {} if len(sec_str) > 1: kwargs['name'] = ' '.join(sec_str[1:]) # elaborate class and keyword arguments bad_ndata = False for k, v in cfg.items(sec): # read items if k is None or v is None or k == '' or v == '': continue elif k in ['position', 'orientation', 'trajectory']: if v == 'False': kwargs[k] = False elif v == 'Truse': kwargs[k] = True else: kwargs[k] = map(float, v.split()) elif k in ['neuron_data']: kwargs[k] = v ndata_path_list = [ osp.join(path, v) for path in ndata_paths ] added_ndata = self.neuron_data.insert(ndata_path_list) if added_ndata == 0: bad_ndata = True else: kwargs[k] = v # delegate action if bad_ndata: continue elif cls == 'Neuron': self.register_neuron(**kwargs) elif cls == 'Tetrode': self.register_recorder(**kwargs) def scene_config_save(self, fname): """save the current scene configuration to a file :Parameters: fname : str Path to save tha cfg to. """ # checks and inits cfg = ConfigParser() # write CONFIG section cfg.add_section('CONFIG') cfg.set('CONFIG', 'frame_size', self.frame_size) cfg.set('CONFIG', 'sample_rate', self.sample_rate) ndata_paths = '\t\n'.join(self.neuron_data.paths) cfg.set('CONFIG', 'neuron_data_dir', ndata_paths) # helper functions npy2cfg = lambda x: ' '.join(map(str, x.tolist())) # add per SimObject for obj in self.values(): if isinstance(obj, Neuron): name = 'Neuron %s' % obj.name cfg.add_section(name) cfg.set(name, 'cluster', str(self.cls_dyn.get_cls_for_nrn(obj))) cfg.set(name, 'position', npy2cfg(obj.position)) if obj.orientation is True or obj.orientation is False: cfg.set(name, 'orientation', obj.orientation) else: cfg.set(name, 'orientation', npy2cfg(obj.orientation)) cfg.set(name, 'rate_of_fire', str(obj.rate_of_fire)) cfg.set(name, 'amplitude', str(obj.amplitude)) cfg.set(name, 'neuron_data', str(obj._neuron_data.filename)) elif isinstance(obj, Recorder): name = 'Tetrode %s' % obj.name cfg.add_section(name) cfg.set(name, 'position', npy2cfg(obj.position)) cfg.set(name, 'orientation', npy2cfg(obj._trajectory)) cfg.set(name, 'snr', str(obj.snr)) # save save_file = open(fname, 'w') cfg.write(save_file) save_file.close() ## methods config loading def load_config(self, cfg_path=None): """loads initialization values from file :Parameters: cfg_file : str Path to the config file. Should be readable by a ConfigParser. """ # TODO: implement config file handling ## special methods def __len__(self): return len(filter(lambda x: isinstance(x, Neuron), self.values())) def __str__(self): return 'BaseSimulation :: %d items' % len(self)