Source code for netpyne.network.stim

"""
Module for adding stimulations to networks

"""

from numbers import Number

try:
    basestring
except NameError:
    basestring = str


# -----------------------------------------------------------------------------
#  Add stims
# -----------------------------------------------------------------------------
[docs] def addStims(self): """ Internal function to add stims specified in specs.NetParams Usage: Creates and attaches stims to targets via CompartCell.addstim() based on entries in the specs.NetParams sub- dictionaries -- specs.NetParams.stimSourceParams and specs.NetParams.stimTargetParams (see below) NetParams.stimSourceParams entries contain key-value pairs to describe NEURON point processes specified by the 'type' entry (i.e. 'IClamp', 'VClamp', 'SEClamp', 'AlphaSynapse', 'VecStim') NetParams.stimTargetParams entries contain key-value pairs to describe the post-synaptic connections for a stimSourceParam entry specified by the 'source' entry, including a 'sec' and 'loc' entry (describing section and location) for where the post-synaptic connection will exist and a 'conds' entry with a dictionary specifying the cell criteria for the post-synaptic connections: (i.e. 'x', 'y', 'z' or 'xnorm', 'ynorm', 'znorm' specifying cell criteria by location, 'cellList' specifying cell criteria by specific gid, or arbitrary 'key': 'value' tags. For 'VecStim' point processes, it may be more convenient to create an artificial cell (i.e.netParams.popParams see: netpyne/cell/pointCell.py) which allows pattern generation ('rhythmic', 'evoked', 'poisson', 'gauss') by key-value entries in a 'spikePattern' dictionary. Parameters ---------- self : <type> <Short description of self> **Default:** *required* """ from .. import sim sim.timing('start', 'stimsTime') if self.params.stimSourceParams and self.params.stimTargetParams: if sim.rank == 0: print('Adding stims...') if sim.nhosts > 1: # Gather tags from all cells allCellTags = sim._gatherAllCellTags() else: allCellTags = {cell.gid: cell.tags for cell in self.cells} # allPopTags = {i: pop.tags for i,pop in enumerate(self.pops)} # gather tags from pops so can connect NetStim pops sources = self.params.stimSourceParams for targetLabel, target in self.params.stimTargetParams.items(): # for each target parameter set if 'sec' not in target: target['sec'] = None # if section not specified, make None (will be assigned to first section in cell) if 'loc' not in target: target['loc'] = None # if location not specified, make None source = sources.get(target['source']) postCellsTags = allCellTags for condKey, condValue in target['conds'].items(): # Find subset of cells that match postsyn criteria if condKey in ['x', 'y', 'z', 'xnorm', 'ynorm', 'znorm']: postCellsTags = { gid: tags for (gid, tags) in postCellsTags.items() if condValue[0] <= tags.get(condKey, None) < condValue[1] } # dict with post Cell objects} # dict with pre cell tags elif condKey == 'cellList': pass elif isinstance(condValue, list): postCellsTags = { gid: tags for (gid, tags) in postCellsTags.items() if tags.get(condKey, None) in condValue } # dict with post Cell objects else: postCellsTags = { gid: tags for (gid, tags) in postCellsTags.items() if tags.get(condKey, None) == condValue } # dict with post Cell objects # subset of cells from selected pops (by relative indices) if 'cellList' in target['conds']: if isinstance(target['conds']['cellList'],list): orderedPostGids = sorted(postCellsTags.keys()) gidList = [orderedPostGids[i] for i in target['conds']['cellList']] postCellsTags = {gid: tags for (gid, tags) in postCellsTags.items() if gid in gidList} elif target['conds']['cellList']=='all': # it would be: postCellsTags = allCellTags if sim.cfg.verbose: print(' Warning: all cells included in stimulation %s' % (targetLabel)) else: if sim.cfg.verbose: print(' Warning: cellList not valid for stimulation %s' % (targetLabel)) postCellsTags = {} # initialize randomizer in case used in string-based function (see issue #89 for more details) self.rand.Random123( sim.hashStr('stim_' + source['type']), sim.hashList(sorted(postCellsTags)), sim.cfg.seeds['stim'] ) # calculate params if string-based funcs strParams = self._stimStrToFunc(postCellsTags, source, target) if source['type'] == 'XStim': from neuron import h import numpy as np # Obtaining information needed when adding extracellular stimulation if not sim.net.params.defineCellShapes: sim.net.defineCellShapes() # convert cell shapes (if not previously done already) sim.net.calcSegCoords() # Creating the temporal pattern for external stimulation (common to all cells) times = np.arange(0,sim.cfg.duration,sim.cfg.dt) if 'waveform' in source: if source['waveform']['type']=='pulse' or source['waveform']['type']=='sinusoidal': if 'amp' in source['waveform']: amp = source['waveform']['amp'] else: amp = 0 if 'del' in source['waveform']: t_start = source['waveform']['del'] else: t_start = 0 if 'del' in source['waveform'] and 'dur' in source['waveform']: t_end = source['waveform']['del'] + source['waveform']['dur'] else: t_end = sim.cfg.duration if t_start > sim.cfg.duration: print(" Extracellular stimulation defined beyond simulation time") t_start = 0 if t_end > sim.cfg.duration: print(" Extracellular stimulation defined beyond simulation time") t_end = sim.cfg.duration if source['waveform']['type']=='sinusoidal': if 'freq' in source['waveform']: freq = source['waveform']['freq'] else: print(" Extracellular stimulation (Sinusoidal). No frequency given") freq = 0 # no stimulation signal = np.array([amp*np.sin(2*np.pi*freq*t/1000) if t>t_start and t<t_end else 0 for t in times]) elif source['waveform']['type']=='pulse': signal = np.array([amp if t>t_start and t<t_end else 0 for t in times]) elif source['waveform']['type']=='external': # load time and signal # time try: times_ext_ = source['waveform']['time'] if isinstance(times_ext_,np.ndarray): times_ext = times_ext_.tolist() elif times_ext_.endswith('.pkl'): import pickle with open(times_ext_, 'rb') as input_file: times_ext = pickle.load(input_file) except: print('Extracellular estimulation defined by external file. Please, provide "time"') # signal try: signal_ext_ = source['waveform']['signal'] if isinstance(signal_ext_,np.ndarray): signal_ext = signal_ext_.tolist() elif signal_ext_.endswith('.pkl'): import pickle with open(signal_ext_, 'rb') as input_file: signal_ext = pickle.load(input_file) except: print('Extracellular stimulation defined by external file. Please, provide "signal"') # Checking for simulation time-step and simulation time if len(times_ext)!=len(signal_ext): print("Extracellular stimulation has different dimensions for time and value") dt_ext = times_ext[1]-times_ext[0] # assuming constant time-step if dt_ext != sim.cfg.dt: print("Please, accomodate external extracellular signal to simulation timestep") if len(times_ext) > len(times): times = np.array(times_ext[0:len(times)]) # rewrite times np array signal = np.array(signal_ext[0:len(times)]) elif len(times_ext) < len(times): signal = [0]*len(times) for nn in range(len(signal_ext)): signal[nn] = signal_ext[nn] signal = np.array(signal) else: times = np.array(times_ext) # rewrite times np array signal = np.array(signal_ext) else: signal = np.array([0 if t>t_start and t<t_end else 0 for t in times]) print(" Extracellular stimulation. Waveform not recognized") self.t = h.Vector(times.tolist()) if 'mod_based' in source and source['mod_based']==True: # when using xtra.mod self.stim = h.Vector(signal.tolist()) try: self.stim.play(h._ref_is_xtra, self.t ) except: print("Extracellular stimulation with 'mod_based' requires compilation of xtra.mod with a global variable called 'is'") # loop over postCells and add stim target for postCellGid in postCellsTags: # for each postsyn cell if postCellGid in self.gid2lid: # check if postsyn is in this node's list of gids postCell = self.cells[sim.net.gid2lid[postCellGid]] # get Cell object # stim target params params = {} params['label'] = targetLabel params['source'] = target['source'] params['sec'] = strParams['secList'][postCellGid] if 'secList' in strParams else target['sec'] params['loc'] = strParams['locList'][postCellGid] if 'locList' in strParams else target['loc'] if source['type'] == 'NetStim': # for NetStims add weight+delay or default values params['weight'] = ( strParams['weightList'][postCellGid] if 'weightList' in strParams else target.get('weight', 1.0) ) params['delay'] = ( strParams['delayList'][postCellGid] if 'delayList' in strParams else target.get('delay', 1.0) ) params['synsPerConn'] = ( strParams['synsPerConnList'][postCellGid] if 'synsPerConnList' in strParams else target.get('synsPerConn', 1) ) params['synMech'] = target.get('synMech', None) for p in ['Weight', 'Delay', 'Loc']: if 'synMech' + p + 'Factor' in target: params['synMech' + p + 'Factor'] = target.get('synMech' + p + 'Factor') if 'originalFormat' in source and source['originalFormat'] == 'NeuroML2': if 'weight' in target: params['weight'] = target['weight'] for sourceParam in source: # copy source params params[sourceParam] = ( strParams[sourceParam + 'List'][postCellGid] if sourceParam + 'List' in strParams else source.get(sourceParam) ) if source['type'] == 'NetStim': self._addCellStim(params, postCell) # call method to add connections (sort out synMechs first) elif source['type'] == 'XStim': # Adding extracellular stimulation params['segCoords'] = postCell._segCoords if not('mod_based' in source and source['mod_based']==True): # these are not used when compiling the xtra.mod, as it is already played a h.Vector with a global temporal stimulation params['time'] = self.t params['stim'] = signal postCell.addStim(params) # call cell method to add connection else: postCell.addStim(params) # call cell method to add connection print((' Number of stims on node %i: %i ' % (sim.rank, sum([len(cell.stims) for cell in self.cells])))) sim.pc.barrier() sim.timing('stop', 'stimsTime') if sim.rank == 0 and sim.cfg.timing: print((' Done; cell stims creation time = %0.2f s.' % sim.timingData['stimsTime'])) return [cell.stims for cell in self.cells]
# ----------------------------------------------------------------------------- # Set parameters and add stim # ----------------------------------------------------------------------------- def _addCellStim(self, stimParam, postCell): # convert synMech param to list (if not already) if not isinstance(stimParam.get('synMech'), list): stimParam['synMech'] = [stimParam.get('synMech')] # generate dict with final params for each synMech paramPerSynMech = ['weight', 'delay', 'loc'] finalParam = {} for i, synMech in enumerate(stimParam.get('synMech')): for param in paramPerSynMech: finalParam[param + 'SynMech'] = stimParam.get(param) if len(stimParam['synMech']) > 1: if isinstance(stimParam.get(param), list): # get weight from list for each synMech finalParam[param + 'SynMech'] = stimParam[param][i] elif 'synMech' + param.title() + 'Factor' in stimParam: # adapt weight for each synMech finalParam[param + 'SynMech'] = ( stimParam[param] * stimParam['synMech' + param.title() + 'Factor'][i] ) params = {k: stimParam.get(k) for k, v in stimParam.items()} params['synMech'] = synMech params['loc'] = finalParam['locSynMech'] params['weight'] = finalParam['weightSynMech'] params['delay'] = finalParam['delaySynMech'] postCell.addStim(params=params) # ----------------------------------------------------------------------------- # Convert stim param string to function # ----------------------------------------------------------------------------- def _stimStrToFunc(self, postCellsTags, sourceParams, targetParams): # list of params that have a function passed in as a string # params = sourceParams+targetParams params = sourceParams.copy() params.update(targetParams) paramsStrFunc = [ param for param in self.stimStringFuncParams + self.connStringFuncParams if param in params and isinstance(params[param], basestring) and params[param] not in ['variable'] ] # dict to store correspondence between string and actual variable dictVars = {} dictVars['post_x'] = lambda postConds: postConds['x'] dictVars['post_y'] = lambda postConds: postConds['y'] dictVars['post_z'] = lambda postConds: postConds['z'] dictVars['post_xnorm'] = lambda postConds: postConds['xnorm'] dictVars['post_ynorm'] = lambda postConds: postConds['ynorm'] dictVars['post_znorm'] = lambda postConds: postConds['znorm'] dictVars['rand'] = lambda unused1: self.rand # add netParams variables for k, v in self.params.__dict__.items(): if isinstance(v, Number): dictVars[k] = v # for each parameter containing a function, calculate lambda function and arguments from netpyne.specs.utils import generateStringFunction strParams = {} for paramStrFunc in paramsStrFunc: strFunc = params[paramStrFunc] # string containing function lambdaFunc, strVars = generateStringFunction(strFunc, list(dictVars.keys())) # store lambda function and func vars in connParam (for weight, delay and synsPerConn since only calculated for certain conns) params[paramStrFunc + 'Func'] = lambdaFunc params[paramStrFunc + 'FuncVars'] = {strVar: dictVars[strVar] for strVar in strVars} # replace lambda function (with args as dict of lambda funcs) with list of values strParams[paramStrFunc + 'List'] = { postGid: params[paramStrFunc + 'Func']( **{ k: v if isinstance(v, Number) else v(postCellTags) for k, v in params[paramStrFunc + 'FuncVars'].items() } ) for postGid, postCellTags in sorted(postCellsTags.items()) } return strParams