diff --git a/netpyne/cell/cell.py b/netpyne/cell/cell.py index 0f85b66c2..62359522c 100644 --- a/netpyne/cell/cell.py +++ b/netpyne/cell/cell.py @@ -124,6 +124,38 @@ def _shapeStim(self, isi=1, variation=0, width=0.05, weight=10, start=0, finish= stimvecs = deepcopy([fulltime, fulloutput, events]) # Combine vectors into a matrix return stimvecs + + def _connWeightsAndDelays(self, params, netStimParams): + from .. import sim + + # Scale factor for connection weights + if netStimParams: + scaleFactor = sim.net.params.scaleConnWeightNetStims + else: + connWeights = sim.net.params.scaleConnWeightModels + if isinstance(connWeights, dict) and (self.tags['cellModel'] in connWeights): + # use scale factor specific for this cell model + scaleFactor = connWeights[self.tags['cellModel']] + else: + scaleFactor = sim.net.params.scaleConnWeight # use global scale factor + + synsPerConn = params['synsPerConn'] + + # Weights + if isinstance(params['weight'], list): + assert len(params['weight']) == synsPerConn, 'Number of weights must match synsPerConn' + weights = [scaleFactor * w for w in params['weight']] + else: + weights = [scaleFactor * params['weight']] * synsPerConn + + # Delays + if isinstance(params['delay'], list): + assert len(params['delay']) == synsPerConn, 'Number of delays must match synsPerConn' + delays = params['delay'] + else: + delays = [params['delay']] * synsPerConn + + return weights, delays def addNetStim(self, params, stimContainer=None): from .. import sim diff --git a/netpyne/cell/compartCell.py b/netpyne/cell/compartCell.py index e0835c880..21fb180ac 100644 --- a/netpyne/cell/compartCell.py +++ b/netpyne/cell/compartCell.py @@ -852,8 +852,9 @@ def addConn(self, params, netStimParams=None): params['delay'] = sim.net.params.defaultDelay # if no delay, set default if params.get('preLoc') is None: params['preLoc'] = 0.5 # if no preLoc, set default - if params.get('loc') is None: - params['loc'] = 0.5 # if no loc, set default + + # same logic for `loc` is no longer here because we want to differ default case from explicitly stating 0.5 by user + if params.get('synsPerConn') is None: params['synsPerConn'] = 1 # if no synsPerConn, set default @@ -880,15 +881,10 @@ def addConn(self, params, netStimParams=None): ) return # if self-connection return - # Weight - weights = self._setConnWeights(params, netStimParams, secLabels) - weightIndex = 0 # set default weight matrix index + # Weights and delays: + weights, delays = self._connWeightsAndDelays(params, netStimParams) - # Delays - if isinstance(params['delay'], list): - delays = params['delay'] - else: - delays = [params['delay']] * params['synsPerConn'] + weightIndex = 0 # set default weight matrix index # Check if target is point process (artificial cell) with V not in section pointp, weightIndex = self._setConnPointP(params, secLabels, weightIndex) @@ -918,6 +914,7 @@ def addConn(self, params, netStimParams=None): if pointerParams: if isPreToPostPointerConn: # generate new ids + # TODO: will it even works with synsPerConn > 1? (in particular, loc in params now can be None) preToPostPointerId, postToPrePointerId = self.__generatePointerIds(pointerParams, params) else: # use pre-generated @@ -1573,30 +1570,6 @@ def _setConnSections(self, params): return secLabels - def _setConnWeights(self, params, netStimParams, secLabels): - from .. import sim - - if netStimParams: - scaleFactor = sim.net.params.scaleConnWeightNetStims - elif ( - isinstance(sim.net.params.scaleConnWeightModels, dict) - and sim.net.params.scaleConnWeightModels.get(self.tags['cellModel'], None) is not None - ): - scaleFactor = sim.net.params.scaleConnWeightModels[ - self.tags['cellModel'] - ] # use scale factor specific for this cell model - else: - scaleFactor = sim.net.params.scaleConnWeight # use global scale factor - - if isinstance(params['weight'], list): - weights = [scaleFactor * w for w in params['weight']] - if len(weights) == 1: - weights = [weights[0]] * params['synsPerConn'] - else: - weights = [scaleFactor * params['weight']] * params['synsPerConn'] - - return weights - def _setConnPointP(self, params, secLabels, weightIndex): from .. import sim @@ -1631,16 +1604,14 @@ def _setConnPointP(self, params, secLabels, weightIndex): def _setConnSynMechs(self, params, secLabels): from .. import sim - - distributeSynsUniformly = params.get('distributeSynsUniformly', sim.cfg.distributeSynsUniformly) connRandomSecFromList = params.get('connRandomSecFromList', sim.cfg.connRandomSecFromList) + distributeSynsUniformly = params.get('distributeSynsUniformly', sim.cfg.distributeSynsUniformly) synsPerConn = params['synsPerConn'] if not params.get('synMech'): if sim.net.params.synMechParams: # if no synMech specified, but some synMech params defined - synLabel = list(sim.net.params.synMechParams.keys())[ - 0 - ] # select first synMech from net params and add syn + # select first synMech from net params and add syn: + synLabel = list(sim.net.params.synMechParams.keys())[0] params['synMech'] = synLabel if sim.cfg.verbose: print( @@ -1648,92 +1619,104 @@ def _setConnSynMechs(self, params, secLabels): % (self.gid, synLabel) ) else: # if no synaptic mechanism specified and no synMech params available - if sim.cfg.verbose: - print(' Error: no synaptic mechanisms available to add conn on cell gid=%d ' % (self.gid)) - return -1 # if no Synapse available print error and exit + _ensure(False, params, f"no synaptic mechanisms available to add conn on cell gid={self.gid}") - # if desired synaptic mechanism specified in conn params - if synsPerConn > 1: # if more than 1 synapse - if len(secLabels) == 1: # if single section, create all syns there - synMechSecs = [secLabels[0]] * synsPerConn # same section for all - if isinstance(params['loc'], list): - if len(params['loc']) == synsPerConn: - synMechLocs = params['loc'] - else: - if sim.cfg.verbose: - print( - "Error: The length of the list of locations does not match synsPerConn (distributing uniformly)" - ) - synMechSecs, synMechLocs = self._distributeSynsUniformly( - secList=secLabels, numSyns=synsPerConn - ) - else: - synMechLocs = [i * (1.0 / synsPerConn) + 1.0 / synsPerConn / 2 for i in range(synsPerConn)] - else: - # if multiple sections, distribute syns uniformly - if distributeSynsUniformly: - synMechSecs, synMechLocs = self._distributeSynsUniformly(secList=secLabels, numSyns=synsPerConn) - else: - # have list of secs that matches num syns - if not connRandomSecFromList and synsPerConn == len(secLabels): - synMechSecs = secLabels - if isinstance(params['loc'], list): - if len(params['loc']) == synsPerConn: # list of locs matches num syns - synMechLocs = params['loc'] - else: # list of locs does not match num syns - print("Error: The length of the list of locations does not match synsPerConn (with distributeSynsUniformly = False)") - return - else: # single loc - synMechLocs = [params['loc']] * synsPerConn - else: - synMechSecs = secLabels - synMechLocs = params['loc'] if isinstance(params['loc'], list) else [params['loc']] - - # randomize the section to connect to and move it to beginning of list - if connRandomSecFromList and len(synMechSecs) >= synsPerConn: - if len(synMechLocs) == 1: - synMechLocs = [params['loc']] * synsPerConn - rand = h.Random() - preGid = params['preGid'] if isinstance(params['preGid'], int) else 0 - rand.Random123(sim.hashStr('connSynMechsSecs'), self.gid, preGid) # initialize randomizer - - randSecPos = sim.net.randUniqueInt(rand, synsPerConn, 0, len(synMechSecs) - 1) - synMechSecs = [synMechSecs[i] for i in randSecPos] - - if isinstance(params['loc'], list): - randLocPos = sim.net.randUniqueInt(rand, synsPerConn, 0, len(synMechLocs) - 1) - synMechLocs = [synMechLocs[i] for i in randLocPos] - else: - rand.uniform(0, 1) - synMechLocs = [rand.repick() for i in range(synsPerConn)] - else: - print("\nError: The length of the list of sections needs to be greater or equal to the synsPerConn (with connRandomSecFromList = True)") - return + # make difference between loc explicitly specified by user vs set by default + if params.get('loc') is None: + params['loc'] = 0.5 + isExplicitLoc = False + else: + isExplicitLoc = True # keep track is loc was statet explicitly (for proper error/warning handling) + if synsPerConn > 1: # if more than 1 synapse + synMechSecs, synMechLocs = self._secsAndLocsForMultisynapse(params, isExplicitLoc, secLabels, connRandomSecFromList, distributeSynsUniformly) else: # if 1 synapse - # by default place on 1st section of list and location available - synMechSecs = secLabels - synMechLocs = params['loc'] if isinstance(params['loc'], list) else [params['loc']] - - # randomize the section to connect to and move it to beginning of list - if connRandomSecFromList and len(synMechSecs) > 1: - rand = h.Random() - preGid = params['preGid'] if isinstance(params['preGid'], int) else 0 - rand.Random123(sim.hashStr('connSynMechsSecs'), self.gid, preGid) # initialize randomizer - pos = int(rand.discunif(0, len(synMechSecs) - 1)) - synMechSecs[pos], synMechSecs[0] = synMechSecs[0], synMechSecs[pos] - if len(synMechLocs) > 1: - synMechLocs[pos], synMechLocs[0] = synMechLocs[0], synMechLocs[pos] + synMechSecs, synMechLocs = self._secsAndLocsForSingleSynapse(params, secLabels, connRandomSecFromList) # add synaptic mechanism to section based on synMechSecs and synMechLocs (if already exists won't be added unless nonLinear set to True) - synMechs = [ - self.addSynMech( - synLabel=params['synMech'], secLabel=synMechSecs[i], loc=synMechLocs[i], preLoc=params['preLoc'] - ) - for i in range(synsPerConn) - ] + synMechs = [] + for i in range(synsPerConn): + synMech = self.addSynMech(synLabel=params['synMech'], secLabel=synMechSecs[i], loc=synMechLocs[i], preLoc=params['preLoc']) + synMechs.append(synMech) return synMechs, synMechSecs, synMechLocs + def _secsAndLocsForSingleSynapse(self, params, secLabels, connRandomSecFromList): + + # by default place on 1st section of list and location available + synMechSecs = secLabels + loc = params['loc'] + synMechLocs = loc if isinstance(loc, list) else [loc] + + # randomize the section to connect to and move it to beginning of list + if connRandomSecFromList and len(synMechSecs) > 1: + + _ensure(len(synMechSecs) == len(synMechLocs), params, "With connRandomSecFromList == True and synsPerConn == 1 (defaults), the lengths of the list of locations and the list of sections must be the same, in order to use the same randomly generated index for both") + rand = h.Random() + preGid = params['preGid'] if isinstance(params['preGid'], int) else 0 + rand.Random123(sim.hashStr('connSynMechsSecs'), self.gid, preGid) # initialize randomizer + pos = int(rand.discunif(0, len(synMechSecs) - 1)) + synMechSecs = [synMechSecs[pos]] + synMechLocs = [synMechLocs[pos]] + return synMechSecs, synMechLocs + + def _secsAndLocsForMultisynapse(self, params, isExplicitLoc, secLabels, connRandomSecFromList, distributeSynsUniformly): + + synsPerConn = params['synsPerConn'] + loc = params['loc'] + + if len(secLabels) == 1: # if single section, create all syns there + synMechSecs = [secLabels[0]] * synsPerConn # same section for all + if isinstance(loc, list): + _ensure(len(loc) == synsPerConn, params, "The length of the list of locations does not match synsPerConn") + synMechLocs = loc + else: # single value or no value + _ensure(isExplicitLoc == False, params, f"specifiyng the `loc` as single value when synsPerConn > 1 ({synsPerConn} in your case) is deprecated. To silence this warning, remove `loc` from the connection parameters or provide a list of {synsPerConn} values (per each synMech if they are several).") + synMechLocs = [i / synsPerConn + 1 / synsPerConn / 2 for i in range(synsPerConn)] + else: + # if multiple sections, distribute syns uniformly + if distributeSynsUniformly: + _ensure(isExplicitLoc == False, params, f"specifiyng the `loc` explicitly when distributeSynsUniformly is True and multiple sections provided is deprecated. To silence this warning, remove `loc` from the connection parameters or set distributeSynsUniformly to False.") + synMechSecs, synMechLocs = self._distributeSynsUniformly(secList=secLabels, numSyns=synsPerConn) + else: + if connRandomSecFromList: + synMechSecs, synMechLocs = self._randomSecAndLocFromList(params, synsPerConn, secLabels, loc, isExplicitLoc) + else: + # Deterministic. Need to have lists of secs and locs that matches num syns + if not isinstance(loc, list): + loc = [loc] * synsPerConn + + _ensure(synsPerConn == len(secLabels), params, + f"With connRandomSecFromList = False, the length of the list of sections should match synsPerConn") + _ensure(synsPerConn == len(loc), params, + f"The length of the list of locations does not match synsPerConn (with distributeSynsUniformly = False, connRandomSecFromList = False)") + + synMechSecs = secLabels + synMechLocs = loc + return synMechSecs, synMechLocs + + def _randomSecAndLocFromList(self, params, synsPerConn, secLabels, loc, isExplicitLoc): + rand = h.Random() + preGid = params['preGid'] if isinstance(params['preGid'], int) else 0 + rand.Random123(sim.hashStr('connSynMechsSecs'), self.gid, preGid) # initialize randomizer + + from ..network.conn import randInt + maxval = len(secLabels) - 1 + isUnique = synsPerConn <= maxval + 1 + randSecPos = randInt(rand, N=synsPerConn, vmin=0, vmax=maxval, unique=isUnique) + synMechSecs = [secLabels[i] for i in randSecPos] + + if isinstance(loc, list): + maxval = len(loc) - 1 + isUnique = synsPerConn <= maxval + 1 + randLocPos = randInt(rand, N=synsPerConn, vmin=0, vmax=maxval, unique=isUnique) + synMechLocs = [loc[i] for i in randLocPos] + else: + _ensure(isExplicitLoc == False, params, f"specifiyng the `loc` explicitly when distributeSynsUniformly is False and connRandomSecFromList is True (with multiple sections provided) is deprecated. To silence this warning, remove `loc` from the connection parameters.") + + rand.uniform(0, 1) + synMechLocs = h.Vector(synsPerConn).setrand(rand).to_python() + return synMechSecs, synMechLocs + def _distributeSynsUniformly(self, secList, numSyns): from .. import sim @@ -1904,3 +1887,8 @@ def originSecName(self): def originSec(self): return self.secs[self.originSecName()] + +def _ensure(condition, params, message): + connLabel = params.get('label') + connLabelStr = f'[{connLabel}]' if connLabel else '' + assert condition, f" Error in connParams{connLabelStr}: {message}" diff --git a/netpyne/cell/pointCell.py b/netpyne/cell/pointCell.py index 5d6dcd84a..2182689d0 100644 --- a/netpyne/cell/pointCell.py +++ b/netpyne/cell/pointCell.py @@ -351,27 +351,6 @@ def associateGid(self, threshold=None): del nc # discard netcon sim.net.gid2lid[self.gid] = len(sim.net.gid2lid) - def _setConnWeights(self, params, netStimParams): - from .. import sim - - if netStimParams: - scaleFactor = sim.net.params.scaleConnWeightNetStims - elif ( - isinstance(sim.net.params.scaleConnWeightModels, dict) - and sim.net.params.scaleConnWeightModels.get(self.tags['cellModel'], None) is not None - ): - scaleFactor = sim.net.params.scaleConnWeightModels[ - self.tags['cellModel'] - ] # use scale factor specific for this cell model - else: - scaleFactor = sim.net.params.scaleConnWeight # use global scale factor - - if isinstance(params['weight'], list): - weights = [scaleFactor * w for w in params['weight']] - else: - weights = [scaleFactor * params['weight']] * params['synsPerConn'] - - return weights def addConn(self, params, netStimParams=None): from .. import sim @@ -393,19 +372,13 @@ def addConn(self, params, netStimParams=None): ) return # if self-connection return - # Weight - weights = self._setConnWeights(params, netStimParams) + # Weights and delays + weights, delays = self._connWeightsAndDelays(params, netStimParams) if params.get('weightIndex') is None: params['weightIndex'] = 0 # set default weight matrix index weightIndex = params.get('weightIndex') # note that loop below will overwrite the weights value in weights[i] - # Delays - if isinstance(params['delay'], list): - delays = params['delay'] - else: - delays = [params['delay']] * params['synsPerConn'] - # Create connections for i in range(params['synsPerConn']): diff --git a/netpyne/network/conn.py b/netpyne/network/conn.py index 033f5298f..50e8d1b7d 100644 --- a/netpyne/network/conn.py +++ b/netpyne/network/conn.py @@ -584,9 +584,20 @@ def probConn(self, preCellsTags, postCellsTags, connParam): if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.close() # ----------------------------------------------------------------------------- -# Generate random unique integers +# Generate random integers # ----------------------------------------------------------------------------- -def randUniqueInt(self, r, N, vmin, vmax): +def randInt(r, N, vmin, vmax, unique=False): + + if unique: + return randUniqueInt(r, N, vmin, vmax) + else: + from neuron import h + r.discunif(vmin, vmax) + v = h.Vector(N) + v.setrand(r) + return np.array(v, dtype=int) + +def randUniqueInt(r, N, vmin, vmax): """ Function for/to @@ -614,7 +625,8 @@ def randUniqueInt(self, r, N, vmin, vmax): """ - + assert N <= (vmax - vmin) + 1, \ + f"Error in randUniqueInt: impossible to generate {N} unique integers from [{vmin}, {vmax}]" r.discunif(vmin, vmax) out = [] while len(out) < N: @@ -686,7 +698,7 @@ def convConn(self, preCellsTags, postCellsTags, connParam): ) # num of presyn conns / postsyn cell convergence = max(min(int(round(convergence)), len(preCellsTags) - 1), 0) self.rand.Random123(hashPreCells, postCellGid, sim.cfg.seeds['conn']) # init randomizer - randSample = self.randUniqueInt(self.rand, convergence + 1, 0, len(preCellsTags) - 1) + randSample = randUniqueInt(self.rand, convergence + 1, 0, len(preCellsTags) - 1) # note: randSample[divergence] is an extra value used only if one of the random postGids coincided with the preGid preCellsSample = { @@ -772,7 +784,7 @@ def divConn(self, preCellsTags, postCellsTags, connParam): ) # num of presyn conns / postsyn cell divergence = max(min(int(round(divergence)), len(postCellsTags) - 1), 0) self.rand.Random123(hashPostCells, preCellGid, sim.cfg.seeds['conn']) # init randomizer - randSample = self.randUniqueInt(self.rand, divergence + 1, 0, len(postCellsTags) - 1) + randSample = randUniqueInt(self.rand, divergence + 1, 0, len(postCellsTags) - 1) # note: randSample[divergence] is an extra value used only if one of the random postGids coincided with the preGid postCellsSample = { @@ -848,44 +860,48 @@ def fromListConn(self, preCellsTags, postCellsTags, connParam): ) for preId, postId in connParam['connList'] } + numConns = len(connParam['connList']) + + weights, delays, secs, locs = [], [], [], [] + preLocs, preSecs = [], [] # for pointer connections (e.g. gap junctions) only + for paramName, paramValList in zip(['weight', 'delay', 'sec', 'loc', 'preLocs', 'preSecs'], + [weights, delays, secs, locs, preLocs, preSecs]): + paramVal = connParam.get(paramName) + if not paramVal: + continue + + if type(paramVal) in (list, tuple): + _ensure(len(paramVal) == numConns, connParam['label'], f"'{paramName}' has to be of the exact same length as 'connList'. Aborting..") + paramValList.extend(paramVal) + else: + paramValList.append(paramVal) - if 'weight' in connParam and isinstance(connParam['weight'], list): - connParam['weightFromList'] = list(connParam['weight']) # if weight is a list, copy to weightFromList - if 'delay' in connParam and isinstance(connParam['delay'], list): - connParam['delayFromList'] = list(connParam['delay']) # if delay is a list, copy to delayFromList - if 'loc' in connParam and isinstance(connParam['loc'], list): - connParam['locFromList'] = list(connParam['loc']) # if delay is a list, copy to locFromList + connList = connParam.pop('connList') - if connParam.get('synsPerConn', 1) == 1: - if isinstance(connParam.get('sec'), list): - connParam['secFromList'] = list(connParam['sec']) - else: - pass # TODO: needs consistent handling + for iconn, (relativePreId, relativePostId) in enumerate(connList): # for each postsyn cell + if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.update(1) - # for pointer connections (e.g. gap junctions) only: - if isinstance(connParam.get('preLoc'), list): - connParam['preLocFromList'] = list(connParam['preLoc']) - if isinstance(connParam.get('preSec'), list): - connParam['preSecFromList'] = list(connParam['preSec']) + _ensure(relativePreId < len(orderedPreGids), connParam['label'], f"cell index in pre population in connection {iconn} is out of range. Aborting..") + _ensure(relativePostId < len(orderedPostGids), connParam['label'], f"cell index in post population in connection {iconn} is out of range. Aborting..") - for iconn, (relativePreId, relativePostId) in enumerate(connParam['connList']): # for each postsyn cell - if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.update(1) preCellGid = orderedPreGids[relativePreId] postCellGid = orderedPostGids[relativePostId] + if postCellGid in self.gid2lid: # check if postsyn is in this node's list of gids - if 'weightFromList' in connParam: - connParam['weight'] = connParam['weightFromList'][iconn] - if 'delayFromList' in connParam: - connParam['delay'] = connParam['delayFromList'][iconn] - if 'locFromList' in connParam: - connParam['loc'] = connParam['locFromList'][iconn] - if 'secFromList' in connParam: - connParam['sec'] = connParam['secFromList'][iconn] - if 'preLocFromList' in connParam: - connParam['preLoc'] = connParam['preLocFromList'][iconn] - if 'preSecFromList' in connParam: - connParam['preSec'] = connParam['preSecFromList'][iconn] + def setConnValFromList(lst, iConn, connKey): + if not lst: return # this param not specified, skip it to use default value later + if len(lst) == 1: # this means the same value is used for all connections + connParam[connKey] = lst[0] + else: + connParam[connKey] = lst[iConn] + + setConnValFromList(weights, iconn, 'weight') + setConnValFromList(delays, iconn, 'delay') + setConnValFromList(locs, iconn, 'loc') + setConnValFromList(secs, iconn, 'sec') + setConnValFromList(preLocs, iconn, 'preLoc') + setConnValFromList(preSecs, iconn, 'preSec') # TODO: consider cfg.allowSelfConns? if preCellGid != postCellGid: # if not self-connection @@ -901,7 +917,7 @@ def _addCellConn(self, connParam, preCellGid, postCellGid, preCellsTags={}): from ..specs.netParams import SynMechParams # set final param values - paramStrFunc = self.connStringFuncParams + paramStrFunc = self.connStringFuncParams # [weight, delay, synsPerConn, loc] finalParam = {} # Set final parameter values; initialize randomizer for string-based funcs that use rand to ensue replicability @@ -928,28 +944,47 @@ def _addCellConn(self, connParam, preCellGid, postCellGid, preCellsTags={}): if not isinstance(connParam.get('synMech'), list): connParam['synMech'] = [connParam.get('synMech')] - # generate dict with final params for each synMech - paramPerSynMech = ['weight', 'delay', 'loc'] - for i, synMech in enumerate(connParam.get('synMech')): - - for param in paramPerSynMech: - finalParam[param + 'SynMech'] = finalParam.get(param) - if len(connParam['synMech']) > 1: - if isinstance(finalParam.get(param), list): # get weight from list for each synMech - finalParam[param + 'SynMech'] = finalParam[param][i] - elif 'synMech' + param.title() + 'Factor' in connParam: # adapt weight for each synMech - finalParam[param + 'SynMech'] = ( - finalParam[param] * connParam['synMech' + param.title() + 'Factor'][i] - ) + synMechs = connParam['synMech'] + numSynMechs = len(synMechs) + + for i, synMech in enumerate(synMechs): + + # weight, delay and loc (and also sec - if corresp. flag is set) are either single value or list of values. If single value, use it for all synMechs. If it is a list, use the value at index i + if connParam.get('distinctSecsPerSynMech', False): + paramNames = ['weight', 'delay', 'loc', 'synsPerConn', 'sec'] + else: + paramNames = ['weight', 'delay', 'loc', 'synsPerConn'] + # keep sec as is to be handled later in CompartCell.addConn() + sec = connParam.get('sec') + + for param in paramNames: + if numSynMechs == 1: + finalParamVal = finalParam.get(param) + else: + if isinstance(finalParam.get(param), list): # get weight/delay/loc from list for each synMech + + _ensure(len(finalParam[param]) == numSynMechs, connParam['label'], f"{param} should be {numSynMechs}-element list or a single value") + finalParamVal = finalParam[param][i] + + elif (f'synMech{param}Factor' in connParam) and (param is not 'sec'): # adapt weight/delay/loc for each synMech + factors = connParam[f'synMech{param}Factor'] + _ensure(len(factors) == numSynMechs, connParam['label'], f"{f'synMech{param}Factor'} should be {numSynMechs}-element list") + _ensure((type(finalParam[param]) in int, float), connParam['label'], f"{params} should be list of numbers") + finalParamVal = finalParam[param] * factors[i] + + else: + finalParamVal = finalParam.get(param) + + finalParam[param + 'SynMech'] = finalParamVal params = { 'preGid': preCellGid, - 'sec': connParam.get('sec'), + 'sec': sec, # TODO: will not work with `distinctSecsPerSynMech`? 'loc': finalParam['locSynMech'], 'synMech': synMech, 'weight': finalParam['weightSynMech'], 'delay': finalParam['delaySynMech'], - 'synsPerConn': finalParam['synsPerConn'], + 'synsPerConn': finalParam['synsPerConnSynMech'], } # if 'threshold' in connParam: params['threshold'] = connParam.get('threshold') # deprecated, use threshold in preSyn cell sec @@ -987,3 +1022,9 @@ def _addCellConn(self, connParam, preCellGid, postCellGid, preCellsTags={}): params['label'] = connParam.get('label') postCell.addConn(params=params) + + +def _ensure(condition, connLabel, message): + connLabelStr = f'[{connLabel}]' if connLabel else '' + assert condition, f" Error in connParams{connLabelStr}: {message}" +