Changeset 1025 for branches

Show
Ignore:
Timestamp:
12/07/11 17:21:15 (6 months ago)
Author:
apdavison
Message:

More work on neo_output branch. neuron and nest backends now both partially work. Still need to work on compatibility (units, number of data points) and multi-segment recordings

Location:
branches/neo_output
Files:
10 modified

Legend:

Unmodified
Added
Removed
  • branches/neo_output/examples/IF_cond_exp.py

    r917 r1025  
    3636     
    3737record_v(ifcell, "Results/IF_cond_exp_%s.v" % simulator_name) 
    38 try: 
    39     record_gsyn(ifcell, "Results/IF_cond_exp_%s.gsyn" % simulator_name) 
    40 except (NotImplementedError, RecordingError): 
    41     pass 
     38#try: 
     39record_gsyn(ifcell, "Results/IF_cond_exp_%s.gsyn" % simulator_name) 
     40#except (NotImplementedError, RecordingError): 
     41#    pass 
    4242run(200.0) 
    4343 
  • branches/neo_output/src/common/procedural_api.py

    r999 r1025  
    5151 
    5252def build_record(variable, simulator): 
     53    if variable == "gsyn": # will be removed in PyNN 0.9 
     54        variable_list = ['gsyn_exc', 'gsyn_inh'] 
     55    else: 
     56        variable_list = [variable] 
    5357    def record(source, filename): 
    5458        """ 
     
    6064        if not isinstance(source, (BasePopulation, Assembly)): 
    6165            source = source.parent 
    62         source._record(variable, to_file=filename) 
    63         # recorder_list is used by end() 
     66        source.record(variable_list) #, to_file=filename) 
     67        # recorders_autowrite is used by end() 
    6468        if isinstance(source, BasePopulation): 
    65             simulator.recorder_list.append(source.recorders[variable])  # this is a bit hackish - better to add to Population.__del__? 
    66         if isinstance(source, Assembly): 
    67             for population in source.populations: 
    68                 simulator.recorder_list.append(population.recorders[variable]) 
     69            populations = [source] 
     70        elif isinstance(source, Assembly): 
     71            populations = source.populations 
     72        for population in populations: 
     73            simulator.write_on_end.append((population, variable_list, filename)) 
     74# NEED TO HANDLE DEPRECATION OF record_v() and record_gsyn() 
    6975    if variable == 'v': 
    7076        record.__name__ = "record_v" 
  • branches/neo_output/src/nest/__init__.py

    r1001 r1025  
    2020import logging 
    2121import tempfile 
    22 from pyNN.recording import files 
    2322from pyNN.nest.cells import NativeCellType, native_cell_type 
    2423from pyNN.nest.synapses import NativeSynapseDynamics, NativeSynapseMechanism 
     
    135134    return rank() 
    136135  
    137 def end(compatible_output=True): 
     136def end(): 
    138137    """Do any necessary cleaning up before exiting.""" 
    139138    global tempdirs 
    140     # And we postprocess the low level files opened by record() 
    141     # and record_v() method 
    142     for recorder in simulator.recorder_list: 
    143         recorder.write(gather=True, compatible_output=compatible_output) 
     139    for (population, variables, filename) in simulator.write_on_end: 
     140        io = recording.get_io(filename) 
     141        population.write_data(io, variables) 
    144142    for tempdir in tempdirs: 
    145143        shutil.rmtree(tempdir) 
    146144    tempdirs = [] 
    147     simulator.recorder_list = [] 
     145    simulator.write_on_end = [] 
    148146 
    149147def run(simtime): 
  • branches/neo_output/src/nest/recording.py

    r1006 r1025  
     1""" 
     2 
     3:copyright: Copyright 2006-2011 by the PyNN team, see AUTHORS. 
     4:license: CeCILL, see LICENSE for details. 
     5""" 
     6 
    17import tempfile 
    28import os 
     
    713from pyNN import recording, errors 
    814from pyNN.nest import simulator 
    9  
    10 VARIABLE_MAP = {'v': ['V_m'], 'gsyn': ['g_ex', 'g_in']} 
    11 REVERSE_VARIABLE_MAP = {'V_m': 'v'} 
     15import neo 
     16from datetime import datetime 
     17import quantities as pq 
     18 
     19VARIABLE_MAP = {'v': 'V_m', 'gsyn_exc': 'g_ex', 'gsyn_inh': 'g_in'} 
     20REVERSE_VARIABLE_MAP = dict((v,k) for k,v in VARIABLE_MAP.items()) 
    1221 
    1322logger = logging.getLogger("PyNN") 
    1423 
    15 # --- For implementation of record_X()/get_X()/print_X() ----------------------- 
     24 
     25def _set_status(obj, parameters): 
     26    """Wrapper around nest.SetStatus() to add a more informative error message.""" 
     27    try: 
     28        nest.SetStatus(obj, parameters) 
     29    except nest.hl_api.NESTError, e: 
     30        raise nest.hl_api.NESTError("%s. Parameter dictionary was: %s" % (e, parameters)) 
     31 
    1632 
    1733class RecordingDevice(object): 
    18     """ 
    19     Now that NEST introduced the multimeter, and does not allow a node to be 
    20     connected to multiple multimeters, most of the functionality of `Recorder` 
    21     has been moved to this class, while `Recorder` is a wrapper to maintain the 
    22     fiction that each recorder only records a single variable. 
    23     """ 
    24     scale_factors = {'V_m': 1, 'g_ex': 0.001, 'g_in': 0.001} 
    25      
    26     def __init__(self, device_type, to_memory=False): 
    27         assert device_type in ("multimeter", "spike_detector") 
    28         self.type      = device_type 
    29         self.device    = nest.Create(device_type) 
    30         self.to_memory = to_memory 
    31         device_parameters = {"withgid": True, "withtime": True} 
    32         if self.type is 'multimeter': 
    33             device_parameters["interval"] = simulator.state.dt 
    34         else: 
    35             device_parameters["precise_times"] = True 
    36             device_parameters["precision"] = simulator.state.default_recording_precision 
     34    """Base class for SpikeDetector and Multimeter""" 
     35     
     36    def __init__(self, device_parameters, to_memory=True): 
     37        # to be called at the end of the subclass __init__ 
     38        device_parameters.update(withgid=True, withtime=True) 
    3739        if to_memory: 
    3840            device_parameters.update(to_file=False, to_memory=True) 
    3941        else: 
    4042            device_parameters.update(to_file=True, to_memory=False) 
    41         try: 
    42             nest.SetStatus(self.device, device_parameters) 
    43         except nest.hl_api.NESTError, e: 
    44             raise nest.hl_api.NESTError("%s. Parameter dictionary was: %s" % (e, device_parameters)) 
     43        self._all_ids = set([]) 
     44        self._connected = False 
     45        simulator.recording_devices.append(self) 
     46        _set_status(self.device, device_parameters) 
     47 
     48    def add_ids(self, new_ids): 
     49        assert not self._connected 
     50        self._all_ids = self._all_ids.union(new_ids) 
     51     
     52    def get_data(self, variable, desired_ids): 
     53        """ 
     54        Return recorded data as a dictionary containing one numpy array for 
     55        each neuron, ids as keys. 
     56        """ 
     57        events = nest.GetStatus(self.device,'events')[0] 
     58        ids = events['senders'] 
     59        values = events[variable] 
     60        data = {} 
     61        for id in desired_ids: 
     62            data[id] = values[ids==id] 
     63        return data 
     64     
     65 
     66class SpikeDetector(RecordingDevice): 
     67    """A wrapper around the NEST spike_detector device""" 
     68    _nest_connect = lambda device, ids: nest.ConvergentConnect() 
     69 
     70    def __init__(self, to_memory=True): 
     71        self.device = nest.Create('spike_detector') 
     72        device_parameters = {       
     73            "precise_times": True, 
     74            "precision": simulator.state.default_recording_precision 
     75        } 
     76        super(SpikeDetector, self).__init__(device_parameters, to_memory) 
     77 
     78    def connect_to_cells(self): 
     79        assert not self._connected 
     80        nest.ConvergentConnect(list(self._all_ids), self.device, model='static_synapse') 
     81        self._connected = True 
     82     
     83    def get_spiketimes(self, desired_ids): 
     84        """ 
     85        Return spike times as a dictionary containing one numpy array for 
     86        each neuron, ids as keys. 
    4587         
    46         self.record_from = [] 
    47         self._local_files_merged = False 
    48         self._gathered = False 
    49         self._connected = False 
    50         self._all_ids = set([]) 
    51         simulator.recording_devices.append(self) 
    52         logger.debug("Created %s with parameters %s" % (device_type, device_parameters)) 
    53  
    54     def __del__(self): 
    55         for name in "_merged_file", "_gathered_file": 
    56             if hasattr(self, name): 
    57                 getattr(self, name).close() 
    58  
    59     def add_variables(self, *variables): 
    60         assert self.type is "multimeter", "Can't add variables to a spike detector" 
    61         self.record_from.extend(variables) 
    62         nest.SetStatus(self.device, {'record_from': self.record_from}) 
    63  
    64     def add_cells(self, new_ids): 
    65         self._all_ids = self._all_ids.union(new_ids) 
     88        Equivalent to `get_data('times', desired_ids)` 
     89        """ 
     90        return self.get_data('times', desired_ids) 
    6691         
     92 
     93class Multimeter(RecordingDevice): 
     94    """A wrapper around the NEST multimeter device""" 
     95    _nest_connect = nest.ConvergentConnect 
     96     
     97    def __init__(self, to_memory=True): 
     98        self.device = nest.Create('multimeter') 
     99        device_parameters = { 
     100            "interval": simulator.state.dt, 
     101        } 
     102        super(Multimeter, self).__init__(device_parameters, to_memory) 
     103 
    67104    def connect_to_cells(self): 
    68         if not self._connected: 
    69             ids = list(self._all_ids) 
    70             if self.type is "spike_detector": 
    71                 nest.ConvergentConnect(ids, self.device, model='static_synapse') 
    72             else: 
    73                 nest.DivergentConnect(self.device, ids, model='static_synapse') 
    74             self._connected = True 
    75  
    76     def in_memory(self): 
    77         """Determine whether data is being recorded to memory.""" 
    78         return nest.GetStatus(self.device, 'to_memory')[0] 
    79      
    80     def events_to_array(self, events): 
    81         """ 
    82         Transform the NEST events dictionary (when recording to memory) to a 
    83         Numpy array. 
    84         """ 
    85         ids = events['senders'] 
    86         times = events['times'] 
    87         if self.type == 'spike_detector': 
    88             data = numpy.array((ids, times)).T 
    89         else: 
    90             data = [ids, times] 
    91             for var in self.record_from: 
    92                 data.append(events[var]) 
    93             data = numpy.array(data).T 
    94         return data 
    95  
    96     def scale_data(self, data): 
    97         """ 
    98         Scale the data to give appropriate units. 
    99         """ 
    100         scale_factors = [RecordingDevice.scale_factors.get(var, 1) 
    101                          for var in self.record_from] 
    102         for i, scale_factor in enumerate(scale_factors): 
    103             column = i+2 # first two columns are id and t, which should not be scaled. 
    104             if scale_factor != 1: 
    105                 data[:, column] *= scale_factor  
    106         return data 
    107  
    108     def add_initial_values(self, data): 
    109         """ 
    110         Add initial values (NEST does not record the value at t=0). 
    111         """ 
    112         logger.debug("Prepending initial values to recorded data") 
    113         initial_values = [] 
    114         for id in self._all_ids: 
    115             initial = [id, 0.0] 
    116             for variable in self.record_from: 
    117                 variable = REVERSE_VARIABLE_MAP.get(variable, variable) 
    118                 try: 
    119                     initial.append(id.get_initial_value(variable)) 
    120                 except KeyError: 
    121                     initial.append(0.0) # unsatisfactory 
    122             initial_values.append(initial)     
    123         if initial_values: 
    124             data = numpy.concatenate((initial_values, data)) 
    125         return data 
    126  
    127     def read_data_from_memory(self, gather, compatible_output): 
    128         """ 
    129         Return memory-recorded data. 
    130          
    131         `gather` -- if True, gather data from all MPI nodes. 
    132         `compatible_output` -- if True, transform the data into the PyNN 
    133                                standard format. 
    134         """ 
    135         data = nest.GetStatus(self.device,'events')[0] 
    136         if compatible_output: 
    137             data = self.events_to_array(data) 
    138             data = self.scale_data(data)   
    139         if gather and simulator.state.num_processes > 1: 
    140             data = recording.gather(data)      
    141             self._gathered_file = tempfile.TemporaryFile() 
    142             numpy.save(self._gathered_file, data) 
    143             self._gathered = True 
    144         return data 
    145      
    146     def read_local_data(self, compatible_output): 
    147         """ 
    148         Return file-recorded data from the local MPI node, merging data from 
    149         different threads if applicable. 
    150          
    151         The merged data is cached, to avoid the overhead of re-merging if the 
    152         method is called again. 
    153         """ 
    154         # what if the method is called with different values of 
    155         # `compatible_output`? Need to cache these separately. 
    156         if self._local_files_merged: 
    157             self._merged_file.seek(0) 
    158             data = numpy.load(self._merged_file) 
    159         else: 
    160             d = nest.GetStatus(self.device)[0] 
    161             if "filenames" in d: 
    162                 nest_files = d['filenames'] 
    163             else: # indicates that run() has not been called. 
    164                 raise errors.NothingToWriteError("No recorder data. Have you called run()?")    
    165             # possibly we can just keep on saving to the end of self._merged_file, instead of concatenating everything in memory 
    166             logger.debug("Concatenating data from the following files: %s" % ", ".join(nest_files)) 
    167             non_empty_nest_files = [filename for filename in nest_files if os.stat(filename).st_size > 0] 
    168             if len(non_empty_nest_files) > 0: 
    169                 data = numpy.concatenate([numpy.loadtxt(nest_file, dtype=float) for nest_file in non_empty_nest_files]) 
    170             if len(non_empty_nest_files) == 0 or data.size == 0: 
    171                 if self.type is "spike_detector": 
    172                     ncol = 2 
    173                 else: 
    174                     ncol = 2 + len(self.record_from) 
    175                 data = numpy.empty([0, ncol]) 
    176             if compatible_output and self.type is not "spike_detector": 
    177                 data = self.scale_data(data) 
    178                 data = self.add_initial_values(data) 
    179             self._merged_file = tempfile.TemporaryFile() 
    180             numpy.save(self._merged_file, data) 
    181             self._local_files_merged = True 
    182         return data 
    183      
    184     def read_data(self, gather, compatible_output, always_local=False): 
    185         """ 
    186         Return file-recorded data. 
    187          
    188         `gather` -- if True, gather data from all MPI nodes. 
    189         `compatible_output` -- if True, transform the data into the PyNN 
    190                                standard format. 
    191                                 
    192         Gathered data is cached, so the MPI communication need only be done 
    193         once, even if the method is called multiple times. 
    194         """ 
    195         # what if the method is called with different values of 
    196         # `compatible_output`? Need to cache these separately. 
    197         if not self.to_memory: 
    198             if gather and simulator.state.num_processes > 1: 
    199                 if self._gathered: 
    200                     logger.debug("Loading previously gathered data from cache") 
    201                     self._gathered_file.seek(0) 
    202                     data = numpy.load(self._gathered_file) 
    203                 else: 
    204                     local_data = self.read_local_data(compatible_output) 
    205                     if always_local: 
    206                         data = local_data # for always_local cells, no need to gather 
    207                     else: 
    208                         logger.debug("Gathering data") 
    209                         data = recording.gather(local_data) 
    210                     logger.debug("Caching gathered data") 
    211                     self._gathered_file = tempfile.TemporaryFile() 
    212                     numpy.save(self._gathered_file, data) 
    213                     self._gathered = True 
    214             else: 
    215                 data = self.read_local_data(compatible_output) 
    216             if len(data.shape) == 1: 
    217                 data = data.reshape((1, data.size)) 
    218             return data 
    219         else: 
    220             return self.read_data_from_memory(gather, compatible_output) 
    221      
    222     def read_subset(self, variables, gather, compatible_output, always_local=False): 
    223         if self.in_memory(): 
    224             data = self.read_data_from_memory(gather, compatible_output) 
    225         else: # in file 
    226             data = self.read_data(gather, compatible_output, always_local) 
    227         indices = [] 
    228         for variable in variables: 
    229             try: 
    230                 indices.append(self.record_from.index(variable)) 
    231             except ValueError: 
    232                 raise Exception("%s not recorded" % variable) 
    233         columns = tuple([0, 1] + [index + 2 for index in indices]) 
    234         return data[:, columns] 
     105        assert not self._connected 
     106        nest.DivergentConnect(self.device, list(self._all_ids), model='static_synapse') 
     107        self._connected = True 
     108     
     109    @property 
     110    def variables(self): 
     111        return set(nest.GetStatus(self.device, 'record_from')[0]) 
     112     
     113    def add_variable(self, variable): 
     114        current_variables = self.variables 
     115        current_variables.add(VARIABLE_MAP.get(variable, variable)) 
     116        _set_status(self.device, {'record_from': list(current_variables)}) 
     117     
     118 
     119 
     120 
     121#class RecordingDevice(object): 
     122#    scale_factors = {'V_m': 1, 'g_ex': 0.001, 'g_in': 0.001} 
     123#     
     124#    def __init__(self, device_type, to_memory=False): 
     125#        assert device_type in ("multimeter", "spike_detector") 
     126#        self.type      = device_type 
     127#        self.device    = nest.Create(device_type) 
     128#        self.to_memory = to_memory 
     129#        device_parameters = {"withgid": True, "withtime": True} 
     130#        if self.type is 'multimeter': 
     131#            device_parameters["interval"] = simulator.state.dt 
     132#        else: 
     133#            device_parameters["precise_times"] = True 
     134#            device_parameters["precision"] = simulator.state.default_recording_precision 
     135#        if to_memory: 
     136#            device_parameters.update(to_file=False, to_memory=True) 
     137#        else: 
     138#            device_parameters.update(to_file=True, to_memory=False) 
     139#        try: 
     140#            nest.SetStatus(self.device, device_parameters) 
     141#        except nest.hl_api.NESTError, e: 
     142#            raise nest.hl_api.NESTError("%s. Parameter dictionary was: %s" % (e, device_parameters)) 
     143#         
     144#        self.record_from = [] 
     145#        self._local_files_merged = False 
     146#        self._gathered = False 
     147#        self._connected = False 
     148#        self._all_ids = set([]) 
     149#        simulator.recording_devices.append(self) 
     150#        logger.debug("Created %s with parameters %s" % (device_type, device_parameters)) 
     151# 
     152#    def __del__(self): 
     153#        for name in "_merged_file", "_gathered_file": 
     154#            if hasattr(self, name): 
     155#                getattr(self, name).close() 
     156# 
     157#    def add_variables(self, *variables): 
     158#        assert self.type is "multimeter", "Can't add variables to a spike detector" 
     159#        self.record_from.extend(variables) 
     160#        nest.SetStatus(self.device, {'record_from': self.record_from}) 
     161# 
     162#    def add_cells(self, new_ids): 
     163#        self._all_ids = self._all_ids.union(new_ids) 
     164#         
     165#    def connect_to_cells(self): 
     166#        if not self._connected: 
     167#            ids = list(self._all_ids) 
     168#            if self.type is "spike_detector": 
     169#                nest.ConvergentConnect(ids, self.device, model='static_synapse') 
     170#            else: 
     171#                nest.DivergentConnect(self.device, ids, model='static_synapse') 
     172#            self._connected = True 
     173# 
     174#    def in_memory(self): 
     175#        """Determine whether data is being recorded to memory.""" 
     176#        return nest.GetStatus(self.device, 'to_memory')[0] 
     177#     
     178#    def events_to_array(self, events): 
     179#        """ 
     180#        Transform the NEST events dictionary (when recording to memory) to a 
     181#        Numpy array. 
     182#        """ 
     183#        ids = events['senders'] 
     184#        times = events['times'] 
     185#        if self.type == 'spike_detector': 
     186#            data = numpy.array((ids, times)).T 
     187#        else: 
     188#            data = [ids, times] 
     189#            for var in self.record_from: 
     190#                data.append(events[var]) 
     191#            data = numpy.array(data).T 
     192#        return data 
     193# 
     194#    def scale_data(self, data): 
     195#        """ 
     196#        Scale the data to give appropriate units. 
     197#        """ 
     198#        scale_factors = [RecordingDevice.scale_factors.get(var, 1) 
     199#                         for var in self.record_from] 
     200#        for i, scale_factor in enumerate(scale_factors): 
     201#            column = i+2 # first two columns are id and t, which should not be scaled. 
     202#            if scale_factor != 1: 
     203#                data[:, column] *= scale_factor  
     204#        return data 
     205# 
     206#    def add_initial_values(self, data): 
     207#        """ 
     208#        Add initial values (NEST does not record the value at t=0). 
     209#        """ 
     210#        logger.debug("Prepending initial values to recorded data") 
     211#        initial_values = [] 
     212#        for id in self._all_ids: 
     213#            initial = [id, 0.0] 
     214#            for variable in self.record_from: 
     215#                variable = REVERSE_VARIABLE_MAP.get(variable, variable) 
     216#                try: 
     217#                    initial.append(id.get_initial_value(variable)) 
     218#                except KeyError: 
     219#                    initial.append(0.0) # unsatisfactory 
     220#            initial_values.append(initial)     
     221#        if initial_values: 
     222#            data = numpy.concatenate((initial_values, data)) 
     223#        return data 
     224# 
     225#    def read_data_from_memory(self, gather, compatible_output): 
     226#        """ 
     227#        Return memory-recorded data. 
     228#         
     229#        `gather` -- if True, gather data from all MPI nodes. 
     230#        `compatible_output` -- if True, transform the data into the PyNN 
     231#                               standard format. 
     232#        """ 
     233#        data = nest.GetStatus(self.device,'events')[0] 
     234#        if compatible_output: 
     235#            data = self.events_to_array(data) 
     236#            data = self.scale_data(data)   
     237#        if gather and simulator.state.num_processes > 1: 
     238#            data = recording.gather(data)      
     239#            self._gathered_file = tempfile.TemporaryFile() 
     240#            numpy.save(self._gathered_file, data) 
     241#            self._gathered = True 
     242#        return data 
     243#     
     244#    def read_local_data(self, compatible_output): 
     245#        """ 
     246#        Return file-recorded data from the local MPI node, merging data from 
     247#        different threads if applicable. 
     248#         
     249#        The merged data is cached, to avoid the overhead of re-merging if the 
     250#        method is called again. 
     251#        """ 
     252#        # what if the method is called with different values of 
     253#        # `compatible_output`? Need to cache these separately. 
     254#        if self._local_files_merged: 
     255#            self._merged_file.seek(0) 
     256#            data = numpy.load(self._merged_file) 
     257#        else: 
     258#            d = nest.GetStatus(self.device)[0] 
     259#            if "filenames" in d: 
     260#                nest_files = d['filenames'] 
     261#            else: # indicates that run() has not been called. 
     262#                raise errors.NothingToWriteError("No recorder data. Have you called run()?")    
     263#            # possibly we can just keep on saving to the end of self._merged_file, instead of concatenating everything in memory 
     264#            logger.debug("Concatenating data from the following files: %s" % ", ".join(nest_files)) 
     265#            non_empty_nest_files = [filename for filename in nest_files if os.stat(filename).st_size > 0] 
     266#            if len(non_empty_nest_files) > 0: 
     267#                data = numpy.concatenate([numpy.loadtxt(nest_file, dtype=float) for nest_file in non_empty_nest_files]) 
     268#            if len(non_empty_nest_files) == 0 or data.size == 0: 
     269#                if self.type is "spike_detector": 
     270#                    ncol = 2 
     271#                else: 
     272#                    ncol = 2 + len(self.record_from) 
     273#                data = numpy.empty([0, ncol]) 
     274#            if compatible_output and self.type is not "spike_detector": 
     275#                data = self.scale_data(data) 
     276#                data = self.add_initial_values(data) 
     277#            self._merged_file = tempfile.TemporaryFile() 
     278#            numpy.save(self._merged_file, data) 
     279#            self._local_files_merged = True 
     280#        return data 
     281#     
     282#    def read_data(self, gather, compatible_output, always_local=False): 
     283#        """ 
     284#        Return file-recorded data. 
     285#         
     286#        `gather` -- if True, gather data from all MPI nodes. 
     287#        `compatible_output` -- if True, transform the data into the PyNN 
     288#                               standard format. 
     289#                                
     290#        Gathered data is cached, so the MPI communication need only be done 
     291#        once, even if the method is called multiple times. 
     292#        """ 
     293#        # what if the method is called with different values of 
     294#        # `compatible_output`? Need to cache these separately. 
     295#        if not self.to_memory: 
     296#            if gather and simulator.state.num_processes > 1: 
     297#                if self._gathered: 
     298#                    logger.debug("Loading previously gathered data from cache") 
     299#                    self._gathered_file.seek(0) 
     300#                    data = numpy.load(self._gathered_file) 
     301#                else: 
     302#                    local_data = self.read_local_data(compatible_output) 
     303#                    if always_local: 
     304#                        data = local_data # for always_local cells, no need to gather 
     305#                    else: 
     306#                        logger.debug("Gathering data") 
     307#                        data = recording.gather(local_data) 
     308#                    logger.debug("Caching gathered data") 
     309#                    self._gathered_file = tempfile.TemporaryFile() 
     310#                    numpy.save(self._gathered_file, data) 
     311#                    self._gathered = True 
     312#            else: 
     313#                data = self.read_local_data(compatible_output) 
     314#            if len(data.shape) == 1: 
     315#                data = data.reshape((1, data.size)) 
     316#            return data 
     317#        else: 
     318#            return self.read_data_from_memory(gather, compatible_output) 
     319#     
     320#    def read_subset(self, variables, gather, compatible_output, always_local=False): 
     321#        if self.in_memory(): 
     322#            data = self.read_data_from_memory(gather, compatible_output) 
     323#        else: # in file 
     324#            data = self.read_data(gather, compatible_output, always_local) 
     325#        indices = [] 
     326#        for variable in variables: 
     327#            try: 
     328#                indices.append(self.record_from.index(variable)) 
     329#            except ValueError: 
     330#                raise Exception("%s not recorded" % variable) 
     331#        columns = tuple([0, 1] + [index + 2 for index in indices]) 
     332#        return data[:, columns] 
    235333 
    236334 
     
    242340                     'gsyn': 0.001} # units conversion 
    243341     
    244     def __init__(self, variable, population=None, file=None): 
     342    def __init__(self, population, file=None): 
    245343        __doc__ = recording.Recorder.__doc__ 
    246         recording.Recorder.__init__(self, variable, population, file) 
    247         self._create_device() 
     344        recording.Recorder.__init__(self, population, file) 
     345        self._multimeter = Multimeter() 
     346        self._spike_detector = SpikeDetector() 
     347#        self._create_device() 
     348 
     349#    def _create_device(self, variable): 
     350#        to_memory = (self.file is False) # note file=None means we save to a temporary file, not to memory 
     351#        if variable is "spikes": 
     352#            self._device = RecordingDevice("spike_detector", to_memory) 
     353#        else: 
     354#            self._device = None 
     355#            for recorder in self.population.recorders.values(): 
     356#                if hasattr(recorder, "_device") and recorder._device is not None and recorder._device.type == 'multimeter': 
     357#                    self._device = recorder._device 
     358#                    break 
     359#            if self._device is None: 
     360#                self._device = RecordingDevice("multimeter", to_memory) 
     361#            self._device.add_variables(*VARIABLE_MAP.get(self.variable, [self.variable])) 
     362 
     363    def _record(self, variable, new_ids): 
     364        """ 
     365        Add the cells in `new_ids` to the set of recorded cells for the given 
     366        variable. Since a given node can only be recorded from by one multimeter 
     367        (http://www.nest-initiative.org/index.php/Analog_recording_with_multimeter, 14/11/11) 
     368        we record all analog variables for all requested cells.  
     369        """ 
     370        if variable == 'spikes': 
     371            self._spike_detector.add_ids(new_ids) 
     372        else: 
     373            self._multimeter.add_variable(variable) 
     374            self._multimeter.add_ids(new_ids) 
     375 
     376    #def _reset(self): 
     377    #    """ """ 
     378    #    try: 
     379    #        simulator.recording_devices.remove(self._device) 
     380    #    except ValueError: 
     381    #        pass 
     382    #     
     383    #    if self._device != None: 
     384    #          recorders_to_reset=[] 
     385    #          for recorder in self.population.recorders.values(): 
     386    #              if hasattr(recorder, "_device") and recorder._device == self._device: 
     387    #                 recorders_to_reset.append(recorder) 
     388    #          for recorder in recorders_to_reset: 
     389    #              recorder._device = None  
     390    #    self._create_device() 
     391 
     392    def _get(self, variables, gather=False, filter_ids=None): 
     393        """Return the recorded data as a Neo `Block`.""" 
     394        always_local = (hasattr(self.population.celltype, 'always_local') and self.population.celltype.always_local) 
     395        data = neo.Block() 
     396        # TODO: add cached data from previous runs 
     397        data.segments.append(self._get_current_segment(filter_ids=filter_ids, variables=variables)) 
     398        return data 
    248399         
    249     def _create_device(self): 
    250         to_memory = (self.file is False) # note file=None means we save to a temporary file, not to memory 
    251         if self.variable is "spikes": 
    252             self._device = RecordingDevice("spike_detector", to_memory) 
    253         else: 
    254             self._device = None 
    255             for recorder in self.population.recorders.values(): 
    256                 if hasattr(recorder, "_device") and recorder._device is not None and recorder._device.type == 'multimeter': 
    257                     self._device = recorder._device 
    258                     break 
    259             if self._device is None: 
    260                 self._device = RecordingDevice("multimeter", to_memory) 
    261             self._device.add_variables(*VARIABLE_MAP.get(self.variable, [self.variable])) 
    262  
    263     def _record(self, new_ids): 
    264         """Called by record().""" 
    265         self._device.add_cells(new_ids) 
    266  
    267     def _reset(self): 
    268         """ """ 
    269         try: 
    270             simulator.recording_devices.remove(self._device) 
    271         except ValueError: 
    272             pass 
    273          
    274         if self._device != None: 
    275               recorders_to_reset=[] 
    276               for recorder in self.population.recorders.values(): 
    277                   if hasattr(recorder, "_device") and recorder._device == self._device: 
    278                      recorders_to_reset.append(recorder) 
    279               for recorder in recorders_to_reset: 
    280                   recorder._device = None  
    281         self._create_device() 
    282  
    283     def _get(self, gather=False, compatible_output=True, filter=None): 
    284         """Return the recorded data as a Numpy array.""" 
    285         if self._device is None: 
    286             raise errors.NothingToWriteError("No cells recorded, so no data to return") 
    287         always_local = (hasattr(self.population.celltype, 'always_local') and self.population.celltype.always_local) 
    288         if self.variable is "spikes": 
    289             data = self._device.read_data(gather, compatible_output, always_local) 
    290         else: 
    291             variables = VARIABLE_MAP.get(self.variable, [self.variable]) 
    292             data = self._device.read_subset(variables, gather, compatible_output, always_local) 
    293         assert len(data.shape) == 2 
    294         if not self._device._gathered:             
    295             filtered_ids = self.filter_recorded(filter)             
    296             mask = reduce(numpy.add, (data[:,0]==id for id in filtered_ids))                 
    297             if len(data) > 0: 
    298                 data = data[mask] 
    299         return data 
     400    def _get_current_segment(self, filter_ids=None, variables='all'): 
     401        segment = neo.Segment(name=self.population.label, 
     402                              description=self.population.describe(), 
     403                              rec_datetime=datetime.now()) # would be nice to get the time at the start of the recording, not the end 
     404        variables_to_include = set(self.recorded.keys()) 
     405        if variables is not 'all': 
     406            variables_to_include = variables_to_include.intersection(set(variables)) 
     407        for variable in variables_to_include: 
     408            if variable == 'spikes': 
     409                spike_times = self._spike_detector.get_spiketimes(self.filter_recorded('spikes', filter_ids)) 
     410                segment.spiketrains = [ 
     411                    neo.SpikeTrain(spike_times[id], 
     412                                   t_stop=simulator.state.t*pq.ms, 
     413                                   units='ms', 
     414                                   source_population=self.population.label, 
     415                                   source_id=int(id)) # index? 
     416                    for id in self.filter_recorded('spikes', filter_ids)] 
     417            else: 
     418                data = self._multimeter.get_data(VARIABLE_MAP.get(variable, variable), 
     419                                                 self.filter_recorded(variable, filter_ids)) 
     420                segment.analogsignals = [ 
     421                    neo.AnalogSignal(data[id], 
     422                                     units=recording.UNITS_MAP.get(variable, 'dimensionless'), 
     423                                     t_start=simulator.state.t_start*pq.ms, 
     424                                     sampling_period=simulator.state.dt*pq.ms, 
     425                                     name=variable, 
     426                                     source_population=self.population.label, 
     427                                     source_id=int(id)) 
     428                    for id in self.filter_recorded(variable, filter_ids)] 
     429        return segment 
    300430 
    301431    def _local_count(self, filter): 
  • branches/neo_output/src/nest/simulator.py

    r998 r1025  
    1616Attributes: 
    1717    state -- a singleton instance of the _State class. 
    18     recorder_list 
    1918 
    2019All other functions and classes are private, and should not be used by other 
     
    3534 
    3635CHECK_CONNECTIONS = False 
    37 recorder_list = [] 
     36write_on_end = [] # a list of (population, variable, filename) combinations that should be written to file on end() 
    3837recording_devices = [] 
    3938 
     
    5655        self._cache_num_processes = nest.GetKernelStatus()['num_processes'] # avoids blocking if only some nodes call num_processes 
    5756                                                                            # do the same for rank? 
     57        self.t_start = 0.0 
    5858 
    5959    @property 
     
    9999    nest.SetKernelStatus({'time': 0.0}) 
    100100    state.running = False 
     101    state.t_start = 0.0 
    101102 
    102103# --- For implementation of access to individual neurons' parameters -----------  
  • branches/neo_output/src/neuron/__init__.py

    r1001 r1025  
    1111 
    1212from pyNN.random import * 
     13from pyNN import common, core, space, __doc__, standardmodels 
     14from pyNN.recording import get_io 
    1315from pyNN.neuron import simulator 
    14 from pyNN import common, core, space, __doc__ 
    15  
    1616from pyNN.neuron.standardmodels.cells import * 
    1717from pyNN.neuron.connectors import * 
     
    1919from pyNN.neuron.electrodes import * 
    2020from pyNN.neuron.recording import Recorder 
    21 from pyNN import standardmodels 
     21 
    2222import numpy 
    2323import logging 
     
    2626 
    2727logger = logging.getLogger("PyNN") 
     28 
     29 
    2830 
    2931# ============================================================================== 
     
    7173def end(compatible_output=True): 
    7274    """Do any necessary cleaning up before exiting.""" 
    73     for recorder in simulator.recorder_list: 
    74         recorder.write(gather=True, compatible_output=compatible_output) 
    75     simulator.recorder_list = [] 
     75    for (population, variables, filename) in simulator.write_on_end: 
     76        io = get_io(filename) 
     77        population.write_data(io, variables) 
     78    simulator.write_on_end = [] 
    7679    #simulator.finalize() 
    7780         
  • branches/neo_output/src/neuron/simulator.py

    r1006 r1025  
    1717Attributes: 
    1818    state -- a singleton instance of the _State class. 
    19     recorder_list 
    2019 
    2120All other functions and classes are private, and should not be used by other 
     
    3837# Global variables 
    3938nrn_dll_loaded = [] 
    40 recorder_list = [] 
     39write_on_end = [] 
    4140gid_sources = [] 
    4241logger = logging.getLogger("PyNN") 
     
    191190            assert local_minimum_delay >= state.min_delay, \ 
    192191                   "There are connections with delays (%g) shorter than the minimum delay (%g)" % (local_minimum_delay, state.min_delay) 
    193     state.t_start = state.t 
    194192    state.tstop += simtime 
    195193    logger.info("Running the simulation for %g ms" % simtime) 
  • branches/neo_output/src/recording/__init__.py

    r1008 r1025  
    1919from collections import defaultdict 
    2020from pyNN import errors 
    21 from pyNN.recording import files 
     21import neo.io 
    2222try: 
    2323    from mpi4py import MPI 
     
    9494    else: 
    9595        return variables 
     96 
     97def get_io(filename): 
     98    """ 
     99    Return a Neo IO instance, guessing the type based on the filename suffix. 
     100    """ 
     101    if os.path.splitext(filename)[0] in ('.txt', '.ras', '.v', '.gsyn'): 
     102        return neo.io.PyNNTextIO(filename) 
     103    else: # function to be improved later 
     104        return neo.io.PyNNTextIO(filename) 
    96105 
    97106 
  • branches/neo_output/src/standardmodels/cells.py

    r957 r1025  
    9898        'i_offset'   : 0.0,     # Offset current in nA 
    9999    } 
    100     recordable = ['spikes', 'v', 'gsyn'] 
     100    recordable = ['spikes', 'v', 'gsyn_exc', 'gsyn_inh'] 
    101101    default_initial_values = { 
    102102        'v': -65.0, #'v_rest', 
     
    122122        'i_offset'   : 0.0,     # Offset current in nA 
    123123    } 
    124     recordable = ['spikes', 'v', 'gsyn'] 
     124    recordable = ['spikes', 'v', 'gsyn_exc', 'gsyn_inh'] 
    125125    default_initial_values = { 
    126126        'v': -65.0, #'v_rest', 
     
    159159        'q_rr'       : 3000.0   # Quantal relative refractory conductance increase in nS    
    160160    } 
    161     recordable = ['spikes', 'v', 'gsyn'] 
     161    recordable = ['spikes', 'v', 'gsyn_exc', 'gsyn_inh'] 
    162162    default_initial_values = { 
    163163        'v': -65.0, #'v_rest', 
     
    184184        'v_thresh'  :  -55.0      # mV 
    185185    } 
    186     recordable = ['spikes', 'v', 'gsyn'] 
     186    recordable = ['spikes', 'v', 'gsyn_exc', 'gsyn_inh'] 
    187187    default_initial_values = { 
    188188        'v': -65.0, #'v_rest', 
     
    210210        'i_offset'  : 0.0, # nA 
    211211    } 
    212     recordable = ['spikes', 'v', 'gsyn'] 
     212    recordable = ['spikes', 'v', 'gsyn_exc', 'gsyn_inh'] 
    213213    default_initial_values = { 
    214214        'v': -65.0, #'v_rest', 
     
    244244        'tau_syn_I' : 5.0,    # Rise time of the inhibitory synaptic conductance in ms (alpha function). 
    245245    } 
    246     recordable = ['spikes', 'v', 'w', 'gsyn'] 
     246    recordable = ['spikes', 'v', 'w', 'gsyn_exc', 'gsyn_inh'] 
    247247    default_initial_values = { 
    248248        'v': -65.0, #'v_rest', 
     
    279279        'tau_syn_I' : 5.0,    # Decay time constant of the inhibitory synaptic conductance in ms. 
    280280    } 
    281     recordable = ['spikes', 'v', 'w', 'gsyn'] 
     281    recordable = ['spikes', 'v', 'w', 'gsyn_exc', 'gsyn_inh'] 
    282282    default_initial_values = { 
    283283        'v': -65.0, #'v_rest', 
  • branches/neo_output/test/unittests/test_lowlevelapi.py

    r999 r1025  
    3939def test_build_record(): 
    4040    simulator = Mock() 
    41     simulator.recorder_list = [] 
     41    simulator.write_on_end = [] 
    4242    record_function = common.build_record("foo", simulator) 
    4343    assert isfunction(record_function) 
    4444     
    4545    source = BasePopulation() 
    46     source._record = Mock() 
    47     source.recorders = {'foo': Mock()} 
     46    source.record = Mock() 
    4847    record_function(source, "filename") 
    49     source._record.assert_called_with("foo", to_file="filename") 
    50     assert_equal(simulator.recorder_list, [source.recorders['foo']]) 
     48    source.record.assert_called_with(["foo"]) #, to_file="filename") 
     49    assert_equal(simulator.write_on_end, [(source, ['foo'], "filename")]) 
    5150 
    5251def test_build_record_with_assembly(): 
    5352    simulator = Mock() 
    54     simulator.recorder_list = [] 
     53    simulator.write_on_end = [] 
    5554    record_function = common.build_record("foo", simulator) 
    5655    assert isfunction(record_function) 
     
    5958    p2 = BasePopulation() 
    6059    source = common.Assembly(p1, p2) 
    61     source._record = Mock() 
    62     for p in p1, p2: 
    63         p.recorders = {'foo': Mock()} 
     60    source.record = Mock() 
    6461    record_function(source, "filename") 
    65     source._record.assert_called_with("foo", to_file="filename") 
    66     assert_equal(simulator.recorder_list, [p1.recorders['foo'], p2.recorders['foo']]) 
     62    source.record.assert_called_with(["foo"]) #, to_file="filename") 
     63    assert_equal(simulator.write_on_end, [(p1, ['foo'], "filename"), (p2, ['foo'], "filename")]) # not sure this is what we want - won't file get over-written?