From a843915e7fb64ec486abb84aa81e5db21ac167a6 Mon Sep 17 00:00:00 2001 From: Eugenio Urdapilleta Date: Thu, 23 Feb 2023 09:01:08 -0300 Subject: [PATCH 01/19] Revert "Merge branch 'development' of https://github.com/suny-downstate-medical-center/netpyne into development" This reverts commit c38f4825677b3f9215d525eb492b9a1565664275, reversing changes made to f5d3376dd61e33272a98eabefdabcaa5ffd65aa0. --- netpyne/conversion/__init__.py | 2 +- netpyne/conversion/pythonScript.py | 169 +++++++++++------------------ netpyne/sim/__init__.py | 2 +- netpyne/sim/load.py | 6 +- netpyne/sim/save.py | 146 ------------------------- netpyne/sim/wrappers.py | 2 +- tests/unit/test_createPython.py | 41 ------- tests/unit/test_saveModel.py | 107 ------------------ 8 files changed, 68 insertions(+), 407 deletions(-) delete mode 100644 tests/unit/test_createPython.py delete mode 100644 tests/unit/test_saveModel.py diff --git a/netpyne/conversion/__init__.py b/netpyne/conversion/__init__.py index ec3c4a5b9..fafe2312c 100644 --- a/netpyne/conversion/__init__.py +++ b/netpyne/conversion/__init__.py @@ -12,7 +12,7 @@ standard_library.install_aliases() from .neuronPyHoc import importCell, importCellsFromNet, mechVarList, getSecName -from .pythonScript import createPythonScript, createPythonNetParams, createPythonSimConfig +from .pythonScript import createPythonScript from .excel import importConnFromExcel # comment below to avoid import errors diff --git a/netpyne/conversion/pythonScript.py b/netpyne/conversion/pythonScript.py index 462e29d99..1f1c255a2 100644 --- a/netpyne/conversion/pythonScript.py +++ b/netpyne/conversion/pythonScript.py @@ -36,48 +36,6 @@ def createPythonScript(fname, netParams, simConfig): """ - netParams = prepareNetParams(netParams) - simConfig = prepareSimConfig(simConfig) - - writeToFile(fname, netParams, simConfig) - - -def createPythonNetParams(fname, netParams): - - netParams = prepareNetParams(netParams) - writeToFile(fname, netParams=netParams, writeSimulation=False) - - -def createPythonSimConfig(fname, simConfig, varName=None): - - simConfig = prepareSimConfig(simConfig) - if varName: - writeToFile(fname, simConfig=simConfig, simConfigVarName=varName, writeSimulation=False) - else: - writeToFile(fname, simConfig=simConfig, writeSimulation=False) - - -def prepareNetParams(netParams): - - from netpyne import specs - if isinstance(netParams, specs.NetParams): - # convert netpyne.specs.netParams class to dict class - netParams = netParams.todict() - __removeReservedKeys(netParams) # like __str__, __dict__ - return netParams - - -def prepareSimConfig(simConfig): - - from netpyne import specs - if isinstance(simConfig, specs.SimConfig): - # convert netpyne.specs.netParams class to dict class - simConfig = simConfig.todict() - __removeReservedKeys(simConfig) # like __str__, __dict__ - return simConfig - -def writeToFile(fname, netParams=None, simConfig=None, simConfigVarName='simConfig', writeHeader=True, writeSimulation=True): - import sys import json from netpyne import specs @@ -86,6 +44,15 @@ def replace(string): # convert bools and null from json to python return string.replace('true', 'True').replace('false', 'False').replace('null', '""') + def remove(dictionary): + # remove reserved keys such as __str__, __dict__ + if isinstance(dictionary, dict): + for key, value in list(dictionary.items()): + if key.startswith('__'): + dictionary.pop(key) + else: + remove(value) + def addAttrToScript(attr, value, obj_name, class_instance, file): # write line of netpyne code if is different from default value if not hasattr(class_instance, attr) or value != getattr(class_instance, attr): @@ -95,71 +62,61 @@ def header(title, spacer='-'): # writes a header for the section return '\n# ' + title.upper() + ' ' + spacer * (77 - len(title)) + '\n' - try : - with open(fname if fname.endswith('.py') else fname+'.py', 'w') as file: - imports = 'from netpyne import specs' - if writeSimulation: - imports += ', sim' - imports += '\n' - file.write(imports) - if writeHeader: - file.write(header('documentation')) - file.write("''' Please visit: https://www.netpyne.org '''\n") - if netParams and simConfig: - generatedFrom = 'netParams and simConfig objects' - elif netParams and not simConfig: - generatedFrom = 'netParams object' - else: - generatedFrom = 'simConfig object' - file.write(f"# Python script automatically generated by NetPyNE v{__version__} from {generatedFrom}\n") - file.write(header('script', spacer='=')) - - if netParams: - file.write('netParams = specs.NetParams()\n') - - if simConfig: - file.write(f'{simConfigVarName} = specs.SimConfig()\n') - - if netParams: - file.write(header('single valued attributes')) - defaultNetParams = specs.NetParams() - multivalued = ['popParams' , 'cellParams', 'synMechParams', 'connParams', 'stimSourceParams', 'stimTargetParams'] - for key, value in list(netParams.items()): - if key not in multivalued: - addAttrToScript(key, value, 'netParams', defaultNetParams, file) - - file.write(header('network attributes')) - for param in multivalued: - for key, value in list(netParams[param].items()): - file.write("netParams." + param + "['" + key + "'] = " + replace(json.dumps(value, indent=4))+ '\n') - - if simConfig: - file.write(header('network configuration')) - defaultCfg = specs.SimConfig() - for key, value in list(simConfig.items()): - addAttrToScript(key, value, simConfigVarName, defaultCfg, file) - - if writeSimulation: - file.write(header('create simulate analyze network')) - file.write('import sys\n') - file.write('if not "-neuroml" in sys.argv:\n') - file.write(f' sim.createSimulateAnalyze(netParams=netParams, simConfig={simConfigVarName})\n') - file.write('else:\n') - file.write(f' nml_reference = "NetPyNENetwork" if not {simConfigVarName}.filename else {simConfigVarName}.filename\n') - file.write(f' sim.createExportNeuroML2(netParams=netParams, simConfig={simConfigVarName}, reference = nml_reference)\n') - file.write(header('end script', spacer='=')) + if isinstance(netParams, specs.NetParams): + # convert netpyne.specs.netParams class to dict class + netParams = netParams.todict() + if isinstance(simConfig, specs.SimConfig): + simConfig = simConfig.todict() + + # remove reserved keys like __str__, __dict__ + remove(netParams) + remove(simConfig) + + # network parameters + params = ['popParams', 'cellParams', 'synMechParams'] + params += ['connParams', 'stimSourceParams', 'stimTargetParams'] + + try: + with open(fname if fname.endswith('.py') else fname + '.py', 'w') as file: + file.write('from netpyne import specs, sim\n') + file.write(header('documentation')) + file.write("''' Please visit: https://www.netpyne.org '''\n") + file.write( + "# Python script automatically generated by NetPyNE v%s from netParams and simConfig objects\n" + % __version__ + ) + file.write(header('script', spacer='=')) + file.write('netParams = specs.NetParams()\n') + file.write('simConfig = specs.SimConfig()\n') + + file.write(header('single valued attributes')) + for key, value in list(netParams.items()): + if key not in params: + addAttrToScript(key, value, 'netParams', specs.NetParams(), file) + + file.write(header('network attributes')) + for param in params: + for key, value in list(netParams[param].items()): + file.write( + "netParams." + param + "['" + key + "'] = " + replace(json.dumps(value, indent=4)) + '\n' + ) + + file.write(header('network configuration')) + for key, value in list(simConfig.items()): + addAttrToScript(key, value, 'simConfig', specs.SimConfig(), file) + + file.write(header('create simulate analyze network')) + file.write('import sys\n') + file.write('if not "-neuroml" in sys.argv:\n') + file.write(' sim.createSimulateAnalyze(netParams=netParams, simConfig=simConfig)\n') + file.write('else:\n') + file.write(' nml_reference = "NetPyNENetwork" if not simConfig.filename else simConfig.filename\n') + file.write( + ' sim.createExportNeuroML2(netParams=netParams, simConfig=simConfig, reference = nml_reference)\n' + ) + file.write(header('end script', spacer='=')) print(("script saved on " + fname)) except: - print(('error saving file: %s' %(sys.exc_info()[1]))) - - -def __removeReservedKeys(dictionary): - # remove reserved keys such as __str__, __dict__ - if isinstance(dictionary, dict): - for key, value in list(dictionary.items()): - if key.startswith('__'): - dictionary.pop(key) - else: - __removeReservedKeys(value) + print(('error saving file: %s' % (sys.exc_info()[1]))) diff --git a/netpyne/sim/__init__.py b/netpyne/sim/__init__.py index d77b648f7..ab201a263 100644 --- a/netpyne/sim/__init__.py +++ b/netpyne/sim/__init__.py @@ -47,7 +47,7 @@ from .gather import gatherData, _gatherAllCellTags, _gatherAllCellConnPreGids, _gatherCells, gatherDataFromFiles # import saving functions -from .save import saveJSON, saveData, distributedSaveHDF5, compactConnFormat, intervalSave, saveDataInNodes, saveModel +from .save import saveJSON, saveData, distributedSaveHDF5, compactConnFormat, intervalSave, saveDataInNodes # import loading functions from .load import ( diff --git a/netpyne/sim/load.py b/netpyne/sim/load.py index b85cd762c..4b18b1e86 100644 --- a/netpyne/sim/load.py +++ b/netpyne/sim/load.py @@ -523,8 +523,7 @@ def loadModel(path, loadMechs=True, forceCompileMechs=False): if configFile[-3:] == '.py': cfgModule = sim.loadPythonModule(configFile) - configVar = indexData.get('simConfig_variable', 'cfg') - cfg = getattr(cfgModule, configVar) + cfg = cfgModule.cfg else: configVar = indexData.get('simConfig_variable', 'simConfig') cfg = sim.loadSimCfg(configFile, variable=configVar, setLoaded=False) @@ -538,8 +537,7 @@ def loadModel(path, loadMechs=True, forceCompileMechs=False): __main__.cfg = cfg # this is often required by netParams netParamsModule = sim.loadPythonModule(netParamsFile) - paramsVar = indexData.get('netParams_variable', 'netParams') - netParams = getattr(netParamsModule, paramsVar) + netParams = netParamsModule.netParams else: paramsVar = indexData.get('netParams_variable', None) netParams = sim.loadNetParams(netParamsFile, variable=paramsVar, setLoaded=False) diff --git a/netpyne/sim/save.py b/netpyne/sim/save.py index 37d226360..16c4f0ac9 100644 --- a/netpyne/sim/save.py +++ b/netpyne/sim/save.py @@ -745,149 +745,3 @@ def saveDataInNodes(filename=None, saveLFP=True, removeTraces=False, saveFolder= with open('timing.pkl', 'wb') as file: pickle.dump(sim.timing, file) - - -def saveModel(netParams, simConfig, srcPath, dstPath=None, exportNetParamsAsPython=False, exportSimConfigAsPython=False): - - assert (srcPath is not None) or (dstPath is not None), \ - "Either srcPath or dstPath should be non-None" - - import os, shutil, json - from netpyne.conversion import createPythonNetParams, createPythonSimConfig - - originalDir = os.getcwd() - if srcPath: srcPath = os.path.abspath(srcPath) - if dstPath: dstPath = os.path.abspath(dstPath) - - fromScratch = False - if srcPath is None: - # creating model from scratch. - srcPath = dstPath - fromScratch = True - srcDir, srcPath = __getModelDirAndIndex(srcPath) - - if dstPath is None: - # assume that dstPath is same as srcPath, i.e. original netParams and simConfig will be rewritten in-place - dstPath = srcPath - dstDir, dstPath = __getModelDirAndIndex(dstPath) - - obsoleteNetParams, obsoleteCfg = None, None - - modifyExistingModel = (fromScratch == False) and (dstPath == srcPath) - - if fromScratch: - # by convention, saving the model will erase anything that already exists at dstPath - if os.path.exists(dstDir): - shutil.rmtree(dstDir) - - # (re)create dstDir and create /src dir in it where files will be stored by default - os.makedirs(os.path.join(dstDir, 'src')) - # create default index - indexData = { - 'netParams': f"src/netParams{'.py' if exportNetParamsAsPython else '.json'}", - 'simConfig': f"src/cfg{'.py' if exportSimConfigAsPython else '.json'}" - } - else: - writeToSameDir = srcDir == dstDir - - # if saving to same directory but with other index-file (i.e. another version of model), - # need to use some suffix with file names, to avoid rewriting original files - suffix = '' - if writeToSameDir and not modifyExistingModel: - suffix = __inferSuffix(dstPath) - - # Load srcIndex as dict - fileObj = open(srcPath, 'r') - indexData = json.load(fileObj) - - # Modify entry in indexfile if needed - altered = __alteredFilename( - indexData['netParams'], - suffix, exportNetParamsAsPython - ) - if altered: - obsoleteNetParams = indexData['netParams'] - indexData['netParams'] = altered - - # Modify entry in indexfile if needed - altered = __alteredFilename( - indexData['simConfig'], - suffix, exportSimConfigAsPython - ) - if altered: - obsoleteCfg = indexData['simConfig'] - indexData['simConfig'] = altered - - if not writeToSameDir: - # by convention, saving the model will erase anything that already exists at dstPath - if os.path.exists(dstDir): - shutil.rmtree(dstDir) - - # copy the whole file struct to dstPath - shutil.copytree(srcDir, dstDir) - os.chdir(dstDir) - # .. except netParams and/or cfg in case they are to be rewritten - if obsoleteNetParams: - os.remove(obsoleteNetParams) - if obsoleteCfg: - os.remove(obsoleteCfg) - # and except original index file - os.remove(os.path.split(srcPath)[1]) - - netParamsPath = indexData['netParams'] - cfgPath = indexData['simConfig'] - - os.chdir(dstDir) - - # save netParams - if modifyExistingModel and obsoleteNetParams: - os.remove(obsoleteNetParams) - if exportNetParamsAsPython: - createPythonNetParams(netParamsPath, netParams) - else: # json - netParams.save(netParamsPath) - - # save cfg - if modifyExistingModel and obsoleteCfg: - os.remove(obsoleteCfg) - if exportSimConfigAsPython: - createPythonSimConfig(cfgPath, simConfig, varName='cfg') - else: # json - simConfig.save(cfgPath) - - # save new index file if needed - if (modifyExistingModel == False) or (obsoleteNetParams or obsoleteCfg): - from .. import sim - sim.saveJSON(dstPath, indexData) - - os.chdir(originalDir) - -def __getModelDirAndIndex(path): - name, ext = os.path.splitext(path) - if ext == '': # check if directory (isdir() won't work because path may not exist) - # if path is directory, append default name of index-file - path = os.path.join(path, 'index.npjson') - dir = os.path.dirname(path) - return dir, path - -def __inferSuffix(dstPath): - # Suffix to be perpended to new netParams and cg filenames - fName, ext = os.path.splitext(os.path.split(dstPath)[1]) - if fName.startswith('index'): - # if it's 'indexSOMETHING.npjson', use SOMETHING as suffix - return fName[5:] - else: - # use _ + whole filename as suffix - return '_' + fName - -def __alteredFilename(original, suffix, exportAsPython): - filename, ext = os.path.splitext(original) - if exportAsPython and (ext != '.py'): - ext = '.py' - elif not exportAsPython and (ext != '.json'): - ext = '.json' - altered = filename + suffix + ext - if altered != original: - return altered - else: - return None diff --git a/netpyne/sim/wrappers.py b/netpyne/sim/wrappers.py index d3699be93..43776e144 100644 --- a/netpyne/sim/wrappers.py +++ b/netpyne/sim/wrappers.py @@ -339,7 +339,7 @@ def runFromIndexFile(index): if pathToScript is None: from netpyne import sim - cfg, netParams = sim.loadModel(index) + cfg, netParams = sim.loadFromIndexFile(index) sim.createSimulateAnalyze(simConfig=cfg, netParams=netParams) else: import os, sys, importlib diff --git a/tests/unit/test_createPython.py b/tests/unit/test_createPython.py deleted file mode 100644 index f82678f8b..000000000 --- a/tests/unit/test_createPython.py +++ /dev/null @@ -1,41 +0,0 @@ -import pytest -import sys -from netpyne import sim -from netpyne.conversion import createPythonScript, createPythonNetParams, createPythonSimConfig -import __main__ -if '-nogui' not in sys.argv: - sys.argv.append('-nogui') -from tests.examples.utils import pkg_setup - -@pytest.mark.package_data(['doc/source/code', 'mod']) -class TestCreatePython(): - - def test_netParams_and_cfg(self, pkg_setup): - from tut6 import netParams, simConfig - createPythonScript('test_script.py', netParams, simConfig) - del netParams, simConfig # to make sure nothing got cached.. - - import test_script - sim.checkOutput('tut6') - - - def test_netParams(self, pkg_setup): - from tut6 import netParams - createPythonNetParams(fname='test_net_params.py', netParams=netParams) - del netParams - - from test_net_params import netParams - from tut6 import simConfig - sim.createSimulateAnalyze(netParams, simConfig) - sim.checkOutput('tut6') - - - def test_simConfig(self, pkg_setup): - from tut6 import simConfig - createPythonSimConfig(fname='test_sim_config.py', simConfig=simConfig) - del simConfig - - from test_sim_config import simConfig - from tut6 import netParams - sim.createSimulateAnalyze(netParams, simConfig) - sim.checkOutput('tut6') diff --git a/tests/unit/test_saveModel.py b/tests/unit/test_saveModel.py deleted file mode 100644 index 24c763ac7..000000000 --- a/tests/unit/test_saveModel.py +++ /dev/null @@ -1,107 +0,0 @@ -import pytest -import sys, os, shutil -from netpyne import sim, specs -import __main__ -if '-nogui' not in sys.argv: - sys.argv.append('-nogui') -from tests.examples.utils import pkg_setup - -@pytest.mark.package_data(['.', None]) -class TestSaveModel(): - - def test_save_from_scratch(self, pkg_setup): - # create model from netParams and cfg only, i.e. not having anything previously loaded from index - cfg, netParams = specs.SimConfig(), specs.NetParams() - netParams.connParams['PYR->PYR'] = {'postConds': {'cellType': 'PYR_HH'}} - cfg.recordStep = 123 - - sim.saveModel(netParams, cfg, - srcPath=None, - dstPath='/tmp/HybridTut_tmp' - ) - del cfg, netParams - - cfg, netParams = sim.loadModel('/tmp/HybridTut_tmp') - self.checkModelIsModified(netParams, cfg) - - - def test_save_in_place(self, pkg_setup): - # load model, modify netParams and simConfig and save as same model (re-write) - - # First, dublicate original model to avoid affecting code base: - shutil.copytree('examples/HybridTut', 'examples/HybridTut_tmp') - - cfg, netParams = sim.loadModel('examples/HybridTut_tmp') - self.modifyModel(netParams, cfg) - - sim.saveModel(netParams, cfg, - srcPath='examples/HybridTut_tmp', - dstPath=None, # None meaning re-write model at srcPath - ) - del cfg, netParams - - # check model - cfg, netParams = sim.loadModel('examples/HybridTut_tmp') - self.checkModelIsModified(netParams, cfg) - - shutil.rmtree('examples/HybridTut_tmp') - - - def test_save_another_variant(self, pkg_setup): - # load model, modify netParams and simConfig and save to same folder, but as a new model variant (separate index-file) - cfg, netParams = sim.loadModel('examples/HybridTut', loadMechs=False) - self.modifyModel(netParams, cfg) - - sim.saveModel(netParams, cfg, - srcPath='examples/HybridTut', - dstPath='examples/HybridTut/index_new.npjson', - ) - del cfg, netParams - - # check model - cfg, netParams = sim.loadModel('examples/HybridTut/index_new.npjson', loadMechs=False) - self.checkModelIsModified(netParams, cfg) - - # check that original one kept intact - cfg, netParams = sim.loadModel('examples/HybridTut', loadMechs=False) - self.checkModelNotModified(netParams, cfg) - - - def test_save_to_new_folder(self, pkg_setup): - # load model, modify netParams and simConfig and save to new folder - cfg, netParams = sim.loadModel('examples/HybridTut', loadMechs=False) - self.modifyModel(netParams, cfg) - - sim.saveModel(netParams, cfg, - srcPath='examples/HybridTut', - dstPath='examples/HybridTut_tmp2/', - exportNetParamsAsPython=True - ) - del cfg, netParams - - # check model - cfg, netParams = sim.loadModel('examples/HybridTut_tmp2/', loadMechs=False) - self.checkModelIsModified(netParams, cfg) - # check netParams exported as python (not a default json) - assert os.path.exists('examples/HybridTut_tmp2/src/netParams.py') - assert os.path.exists('examples/HybridTut_tmp2/src/netParams.json') == False - - # check that original one kept intact - cfg, netParams = sim.loadModel('examples/HybridTut', loadMechs=False) - self.checkModelNotModified(netParams, cfg) - - shutil.rmtree('examples/HybridTut_tmp2') - - # utility - - def modifyModel(self, netParams, cfg): - netParams.connParams['PYR->PYR']['postConds']['cellType'] = 'PYR_HH' - cfg.recordStep = 123 - - def checkModelIsModified(self, netParams, cfg): - assert netParams.connParams['PYR->PYR']['postConds']['cellType'] == 'PYR_HH' - assert cfg.recordStep == 123 - - def checkModelNotModified(self, netParams, cfg): - assert netParams.connParams['PYR->PYR']['postConds']['cellType'] == ['PYR_HH', 'PYR_Izhi'] - assert cfg.recordStep == 0.025 From ddde8891b49523a9fbadd012e051c08791613b7f Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 24 Feb 2023 13:21:13 -0600 Subject: [PATCH 02/19] Add metadata for sub-cellular connectivity --- netpyne/metadata/metadata.py | 212 +++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) diff --git a/netpyne/metadata/metadata.py b/netpyne/metadata/metadata.py index 3e35c4e4f..e540fc4c1 100644 --- a/netpyne/metadata/metadata.py +++ b/netpyne/metadata/metadata.py @@ -787,6 +787,218 @@ }, }, # --------------------------------------------------------------------------------------------------------------------- + # netParams.subConnParams + # --------------------------------------------------------------------------------------------------------------------- + "subConnParams": { + "label": "Sub-Cellular Connectivity parameters", + "suggestions": "", + "help": "", + "hintText": "", + "children": { + "preConds": { + "label": "Conditions for the presynaptic cells", + "help": "Presynaptic cell conditions defined using attributes/tags and the required value e.g. {'cellType': 'PYR'}. Values can be lists, e.g. {'pop': ['Exc1', 'Exc2']}. For location properties, the list values correspond to the min and max values, e.g. {'ynorm': [0.1, 0.6]}.", + "suggestions": "", + "hintText": "", + "children": { + "pop": { + "label": "Population (multiple selection available)", + "suggestions": "", + "help": "Cells belonging to this population (or list of populations) will be connected pre-synaptically.", + "hintText": "", + }, + "cellType": { + "label": "Cell type (multiple selection available)", + "suggestions": "", + "help": "Ccells with this cell type attribute/tag will be connected pre-synaptically.", + "hintText": "", + }, + "cellModel": { + "label": "Cell model (multiple selection available)", + "suggestions": "", + "help": "Cells with this cell model attribute/tag will be connected pre-synaptically.", + "hintText": "", + }, + "x": { + "label": "Range of x-axis locations", + "suggestions": "", + "help": "Cells within these x-axis locations will be connected pre-synaptically.", + "hintText": "", + }, + "y": { + "label": "Range of y-axis locations", + "suggestions": "", + "help": "Cells within these y-axis locations will be connected pre-synaptically.", + "hintText": "", + }, + "z": { + "label": "Range of z-axis locations", + "suggestions": "", + "help": "Cells within these z-axis locations will be connected pre-synaptically..", + "hintText": "", + }, + "xnorm": { + "label": "Range of normalized x-axis locations", + "suggestions": "", + "help": "Cells within these normalized x-axis locations will be connected pre-synaptically.", + "hintText": "", + }, + "ynorm": { + "label": "Range of normalized y-axis locations", + "suggestions": "", + "help": "Cells within these normalized y-axis locations will be connected pre-synaptically.", + "hintText": "", + }, + "znorm": { + "label": "Range of normalized z-axis locations", + "suggestions": "", + "help": "Cells within these normalized z-axis locations will be connected pre-synaptically.", + "hintText": "", + }, + }, + }, + "postConds": { + "label": "Conditions for the postsynaptic cells", + "help": "Defined as a dictionary with the attributes/tags of the postsynaptic cell and the required values e.g. {'cellType': 'PYR'}. Values can be lists, e.g. {'pop': ['Exc1', 'Exc2']}. For location properties, the list values correspond to the min and max values, e.g. {'ynorm': [0.1, 0.6]}.", + "suggestions": "", + "hintText": "", + "children": { + "pop": { + "label": "Population (multiple selection available)", + "suggestions": "", + "help": "Cells belonging to this population (or list of populations) will be connected post-synaptically.", + "hintText": "", + }, + "cellType": { + "label": "Cell type (multiple selection available)", + "suggestions": "", + "help": "Ccells with this cell type attribute/tag will be connected post-synaptically.", + "hintText": "", + }, + "cellModel": { + "label": "Cell model (multiple selection available)", + "suggestions": "", + "help": "Cells with this cell model attribute/tag will be connected post-synaptically.", + "hintText": "", + }, + "x": { + "label": "Range of x-axis locations", + "suggestions": "", + "help": "Cells within these x-axis locations will be connected post-synaptically.", + "hintText": "", + }, + "y": { + "label": "Range of y-axis locations", + "suggestions": "", + "help": "Cells within these y-axis locations will be connected post-synaptically.", + "hintText": "", + }, + "z": { + "label": "Range of z-axis locations", + "suggestions": "", + "help": "Cells within these z-axis locations will be connected post-synaptically..", + "hintText": "", + }, + "xnorm": { + "label": "Range of normalized x-axis locations", + "suggestions": "", + "help": "Cells within these normalized x-axis locations will be connected post-synaptically.", + "hintText": "", + }, + "ynorm": { + "label": "Range of normalized y-axis locations", + "suggestions": "", + "help": "Cells within these normalized y-axis locations will be connected post-synaptically.", + "hintText": "", + }, + "znorm": { + "label": "Range of normalized z-axis locations", + "suggestions": "", + "help": "Cells within these normalized z-axis locations will be connected post-synaptically.", + "hintText": "", + }, + }, + }, + "groupSynMechs": { + "label": "Synaptic mechanism", + "help": " List of synaptic mechanisms grouped together when redistributing synapses. If omitted, post-synaptic locations of all connections (meeting preConds and postConds) are redistributed independently with a given profile (defined below). If a list is provided, synapses of common connections (same pre- and post-synaptic neurons for each mechanism) are relocated to the same location. For example, [‘AMPA’,’NMDA’].", + "suggestions": "", + "hintText": "", + }, + "sec": { + "label": "Redistributed Synapse Sections", + "help": "List of sections admitting redistributed synapses. If omitted, the section used to redistribute synapses is the soma or, if it does not exist, the first available section in the post-synaptic cell. For example, [‘Adend1’,’Adend2’, ‘Adend3’,’Bdend’].", + "suggestions": "", + "hintText": "soma", + "type": "list(str)", + }, + "density": { + "label": "Type of redistribution", + "help": "", + "suggestions": "", + "hintText": "", + "children": { + "gridY": { + "label": "Depth y-coordinate Positions", + "help": "List of positions in y-coordinate (depth).", + "suggestions": "", + "hintText": "", + "type": "list(int, float)", + }, + "gridX": { + "label": "Depth x-coordinate Positions", + "help": "List of positions in x-coordinate (or z).", + "suggestions": "", + "hintText": "", + "type": "list(int, float)", + }, + "fixedSomaY": { + "label": "Soma's y-coordinate", + "help": "Absolute position y-coordinate of the soma, used to shift gridY (also provided in absolute coordinates).", + "suggestions": "", + "hintText": "", + "type": "int,float", + }, + "gridValues": { + "label": "Synaptic Density", + "help": "One or two-dimensional list expressing the (relative) synaptic density in the coordinates defined by (gridX and) gridY.", + "suggestions": "", + "hintText": "", + "type": "list(int, float)", + }, + "ref_sec": { + "label": "Reference Section", + "help": "Section used as a reference from which distance is computed. If omitted, the section used to reference distances is the soma or, if it does not exist, anything starting with ‘som’ or, otherwise, the first available section in the post-synaptic cell.", + "suggestions": "", + "hintText": "", + "type": "str", + }, + "ref_seg": { + "label": "Reference Segment", + "help": "Segment within the section used to reference the distances. If omitted, it is used a default value (0.5).", + "suggestions": "", + "hintText": "", + "type": "int, float", + }, + "target_distance": { + "label": "Target Distance", + "help": "Target distance from the reference where synapses will be reallocated. If omitted, this value is set to 0. The chosen location will be the closest to this target, between the allowed sections.", + "suggestions": "", + "hintText": "", + "type": "int, float", + }, + "coord": { + "label": "Coordinate's System", + "help": "Coordinates’ system used to compute distance. If omitted, the distance is computed along the dendritic tree. Alternatively, it may be used ‘cartesian’ to calculate the distance in the euclidean space (distance from the reference to the target segment in the cartesian coordinate system).", + "suggestions": "", + "hintText": "", + "type": "int, float", + }, + } + } + }, + }, + # --------------------------------------------------------------------------------------------------------------------- # netParams.stimSourceParams # --------------------------------------------------------------------------------------------------------------------- "stimSourceParams": { From 7d5507bd20c0fcf5dccadd3ba3065aec01d53724 Mon Sep 17 00:00:00 2001 From: aranega Date: Mon, 27 Feb 2023 12:53:12 -0600 Subject: [PATCH 03/19] Change type of geom, mechs and synMechs to accept string functions expressions --- netpyne/metadata/metadata.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/netpyne/metadata/metadata.py b/netpyne/metadata/metadata.py index 3e35c4e4f..7607a45c3 100644 --- a/netpyne/metadata/metadata.py +++ b/netpyne/metadata/metadata.py @@ -363,7 +363,7 @@ "suggestions": "", "help": "", "hintText": "10", - "type": "float", + "type": "func", }, "L": { "label": "Length (um)", @@ -371,7 +371,7 @@ "suggestions": "", "help": "", "hintText": "50", - "type": "float", + "type": "func", }, "Ra": { "label": "Axial resistance, Ra (ohm-cm)", @@ -379,14 +379,14 @@ "suggestions": "", "help": "", "hintText": "100", - "type": "float", + "type": "func", }, "cm": { "label": "Membrane capacitance, cm (uF/cm2)", "suggestions": "", "help": "", "hintText": "1", - "type": "float", + "type": "func", }, "pt3d": { "label": "3D points", @@ -401,7 +401,7 @@ "suggestions": "", "help": "", "hintText": "1", - "type": "float", + "type": "func", }, }, "mechs": { @@ -409,7 +409,7 @@ "help": "Dictionary of density/distributed mechanisms, including the name of the mechanism (e.g. hh or pas) and a list of properties of the mechanism (e.g. {'g': 0.003, 'e': -70}).", "suggestions": "", "hintText": "", - "type": "float", + "type": "func", }, "ions": { "label": "Ions", @@ -539,28 +539,28 @@ "help": "Define the time constant for the first exponential.", "suggestions": "", "hintText": "1", - "type": "float", + "type": "func", }, "tau2": { "label": "Time constant for exponential 2 (ms)", "help": "Define the time constant for the second exponential.", "suggestions": "", "hintText": "5", - "type": "float", + "type": "func", }, "e": { "label": "Reversal potential (mV)", "help": "Reversal potential of the synaptic receptors.", "suggestions": "", "hintText": "0", - "type": "float", + "type": "func", }, "i": { "label": "synaptic current (nA)", "help": "Synaptic current in nA.", "suggestions": "", "hintText": "10", - "type": "float", + "type": "func", }, }, }, From d5ab67bc1d37032907a0252919469609b163c4c0 Mon Sep 17 00:00:00 2001 From: Irene Bernardi <139147765+irenebernardi@users.noreply.github.com> Date: Mon, 7 Aug 2023 14:52:27 +0200 Subject: [PATCH 04/19] added numCells==0 error message --- netpyne/network/pop.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/netpyne/network/pop.py b/netpyne/network/pop.py index b55b5b237..de1ed1262 100644 --- a/netpyne/network/pop.py +++ b/netpyne/network/pop.py @@ -290,7 +290,12 @@ def createCellsDensity(self): self.rand.uniform(0, 1) vec = h.Vector(self.tags['numCells'] * 3) vec.setrand(self.rand) + try: randLocs = np.array(vec).reshape(self.tags['numCells'], 3) # create random x,y,z locations + except Exception as e: + if 'numCells' in self.tags and self.tags['numCells'] == 0: + print("Unable to create network, please validate that cell population > 0 by gradually increasing cfg.scaleDensity in cfg_demo.py (e.g. try 0.1, then 0.15, etc.)") + raise e if sim.net.params.shape == 'cylinder': # Use the x,z random vales From 0a4cc63eed6345457f58adb9eb28c62971809e43 Mon Sep 17 00:00:00 2001 From: Irene Bernardi <139147765+irenebernardi@users.noreply.github.com> Date: Mon, 7 Aug 2023 22:13:43 +0200 Subject: [PATCH 05/19] fixed wrong indentation --- netpyne/network/pop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netpyne/network/pop.py b/netpyne/network/pop.py index de1ed1262..1f9b5d66d 100644 --- a/netpyne/network/pop.py +++ b/netpyne/network/pop.py @@ -291,7 +291,7 @@ def createCellsDensity(self): vec = h.Vector(self.tags['numCells'] * 3) vec.setrand(self.rand) try: - randLocs = np.array(vec).reshape(self.tags['numCells'], 3) # create random x,y,z locations + randLocs = np.array(vec).reshape(self.tags['numCells'], 3) # create random x,y,z locations except Exception as e: if 'numCells' in self.tags and self.tags['numCells'] == 0: print("Unable to create network, please validate that cell population > 0 by gradually increasing cfg.scaleDensity in cfg_demo.py (e.g. try 0.1, then 0.15, etc.)") From acdb44bc9ece5e85c0759d4f66e809ad11834110 Mon Sep 17 00:00:00 2001 From: Irene Bernardi <139147765+irenebernardi@users.noreply.github.com> Date: Mon, 14 Aug 2023 17:40:45 +0200 Subject: [PATCH 06/19] Made error message for numCells==0 more generic --- netpyne/network/pop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netpyne/network/pop.py b/netpyne/network/pop.py index 1f9b5d66d..350631fa2 100644 --- a/netpyne/network/pop.py +++ b/netpyne/network/pop.py @@ -294,7 +294,7 @@ def createCellsDensity(self): randLocs = np.array(vec).reshape(self.tags['numCells'], 3) # create random x,y,z locations except Exception as e: if 'numCells' in self.tags and self.tags['numCells'] == 0: - print("Unable to create network, please validate that cell population > 0 by gradually increasing cfg.scaleDensity in cfg_demo.py (e.g. try 0.1, then 0.15, etc.)") + print("Unable to create network, please validate that cell population > 0") raise e if sim.net.params.shape == 'cylinder': From 0986040b7be38ee9476a92a5917fcbc4e6b3ae5b Mon Sep 17 00:00:00 2001 From: vvbragin Date: Wed, 16 Aug 2023 16:09:10 +0200 Subject: [PATCH 07/19] issue-553: use 0.5 as a default loc in recordTraces, match actual location of synapses' and stim's to those in 'recordTraces' with precision up to segment --- netpyne/cell/cell.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/netpyne/cell/cell.py b/netpyne/cell/cell.py index 9c1d5d71c..171438510 100644 --- a/netpyne/cell/cell.py +++ b/netpyne/cell/cell.py @@ -212,18 +212,24 @@ def recordTraces(self): if conditionsMet: try: ptr = None - if 'loc' in params and params['sec'] in self.secs: + if 'sec' in params and params['sec'] in self.secs: + + if not 'loc' in params: + params['loc'] = 0.5 # if no loc, set default + + sec = self.secs[params['sec']] + seg = sec.hObj(params['loc']) + if 'mech' in params: # eg. soma(0.5).hh._ref_gna ptr = getattr( - getattr(self.secs[params['sec']]['hObj'](params['loc']), params['mech']), + getattr(seg, params['mech']), '_ref_' + params['var'], ) elif 'synMech' in params: # eg. soma(0.5).AMPA._ref_g - sec = self.secs[params['sec']] synMechList = [ synMech for synMech in sec['synMechs'] - if synMech['label'] == params['synMech'] and synMech['loc'] == params['loc'] + if synMech['label'] == params['synMech'] and synMech['hObj'].get_segment() == seg ] # make list with this label/loc ptr = None if len(synMechList) > 0: @@ -236,12 +242,11 @@ def recordTraces(self): ptr = getattr(synMech['hObj'], '_ref_' + params['var']) elif 'stim' in params: # e.g. sim.net.cells[0].stims[0]['hObj'].i if 'sec' in params and 'loc' in params and 'var' in params: - sec = self.secs[params['sec']] stimList = [ stim for stim in self.stims - if stim['label'] == params['stim'] and stim['loc'] == params['loc'] - ] # make list with this label/loc + if stim['label'] == params['stim'] and stim['hObj'].get_segment() == seg + ] # make list with this label/loc (loc's precision up to segment) ptr = None if len(stimList) > 0: if 'index' in params: From dfcb56c1868b3ad3e08579e0df646212f26fe991 Mon Sep 17 00:00:00 2001 From: Irene Bernardi <139147765+irenebernardi@users.noreply.github.com> Date: Mon, 21 Aug 2023 18:06:27 +0200 Subject: [PATCH 08/19] More descriptive error message and correct indentation -Included population name in error message -Included case in which pop < 1 and not just == 0 --- netpyne/network/pop.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/netpyne/network/pop.py b/netpyne/network/pop.py index 350631fa2..ce19b64a4 100644 --- a/netpyne/network/pop.py +++ b/netpyne/network/pop.py @@ -293,9 +293,10 @@ def createCellsDensity(self): try: randLocs = np.array(vec).reshape(self.tags['numCells'], 3) # create random x,y,z locations except Exception as e: - if 'numCells' in self.tags and self.tags['numCells'] == 0: - print("Unable to create network, please validate that cell population > 0") - raise e + if 'numCells' in self.tags and self.tags['numCells'] == 0 or self.tags['numCells'] < 1: + print(f"Unable to create network, please validate that '{self.tags['pop']}' population size is > 0 ") + raise e + if sim.net.params.shape == 'cylinder': # Use the x,z random vales From 55e81a442f6a541655bc50ff93fb52f7317887e1 Mon Sep 17 00:00:00 2001 From: vvbragin Date: Tue, 19 Sep 2023 15:26:49 +0200 Subject: [PATCH 09/19] time series and PSD plots for CSD (along with some related re-factoring) --- CHANGES.md | 4 + examples/LFPrecording/src/cell/cfg.py | 6 +- examples/LFPrecording/src/net/cfg.py | 5 +- examples/dipoleRecording/src/cfg_cell.py | 5 +- examples/dipoleRecording/src/init_cell.py | 4 +- netpyne/analysis/__init__.py | 2 - netpyne/analysis/csd.py | 302 +------- netpyne/analysis/csd_legacy.py | 651 ------------------ netpyne/analysis/lfp.py | 114 +-- netpyne/analysis/tools.py | 8 +- netpyne/analysis/wrapper.py | 2 +- netpyne/plotting/__init__.py | 8 +- netpyne/plotting/plotCSD.py | 30 +- netpyne/plotting/plotCSDPSD.py | 252 ------- netpyne/plotting/plotCSDTimeSeries.py | 273 -------- ...plotLFPTimeSeries.py => plotTimeSeries.py} | 350 +++++++++- .../{plotLFPPSD.py => plotTimeSeriesPSD.py} | 301 +++++++- netpyne/sim/wrappers.py | 4 +- netpyne/support/csd.py | 62 -- 19 files changed, 737 insertions(+), 1646 deletions(-) delete mode 100644 netpyne/analysis/csd_legacy.py delete mode 100644 netpyne/plotting/plotCSDPSD.py delete mode 100644 netpyne/plotting/plotCSDTimeSeries.py rename netpyne/plotting/{plotLFPTimeSeries.py => plotTimeSeries.py} (52%) rename netpyne/plotting/{plotLFPPSD.py => plotTimeSeriesPSD.py} (56%) delete mode 100644 netpyne/support/csd.py diff --git a/CHANGES.md b/CHANGES.md index 313d0e99f..028dd4de7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,10 @@ **New features** +- Time series and PSD plots for current source density (CSD) + +- Added colorbar to CSD plot + - Extended sim.gatherData() with more optional arguments for flexibility - Specify linear/log scale in `plotRatePSD()` diff --git a/examples/LFPrecording/src/cell/cfg.py b/examples/LFPrecording/src/cell/cfg.py index 9cca70b22..4a4b8325c 100644 --- a/examples/LFPrecording/src/cell/cfg.py +++ b/examples/LFPrecording/src/cell/cfg.py @@ -14,7 +14,5 @@ cfg.analysis['plotTraces'] = {'include': [('E',0)], 'oneFigPer':'cell', 'overlay': False, 'figSize': (5,6),'saveFig': True} # Plot recorded traces for this list of cells cfg.analysis['plotLFP'] = {'includeAxon': False, 'plots': ['timeSeries', 'locations'], 'figSize': (7.5,13.5), 'saveFig': True} -#simConfig.analysis['getCSD'] = {'timeRange': [10,45],'spacing_um': 150, 'vaknin': True} -#simConfig.analysis['plotCSD'] = {'timeRange': [10,45]} -#sim.analysis.getCSD(...args...) -#simConfig.analysis['plotCSD'] = {} +# cfg.analysis['plotCSD'] = {'timeRange': [10,45],'spacing_um': 150, 'vaknin': True, 'saveFig': True} +# cfg.analysis['plotCSDTimeSeries'] = {'saveFig': True} diff --git a/examples/LFPrecording/src/net/cfg.py b/examples/LFPrecording/src/net/cfg.py index 1ce8feab7..cf45fb3dc 100644 --- a/examples/LFPrecording/src/net/cfg.py +++ b/examples/LFPrecording/src/net/cfg.py @@ -22,5 +22,6 @@ cfg.analysis['plotLFP'] = {'includeAxon': False, 'figSize': (6,10), 'timeRange': [100,1000], 'saveFig': True} # optional: 'pop': 'E4' #simConfig.analysis['getCSD'] = {'spacing_um': 200, 'timeRange': [100,3000], 'vaknin': True} #simConfig.analysis['plotLFP'] = {'includeAxon': False, 'figSize': (6,10), 'timeRange':[100,900], 'minFreq': 10, 'maxFreq':60, 'norm':1, 'plots': ['spectrogram'], 'showFig': True} -#simConfig.analysis['plotLFP'] = {'includeAxon': False, 'figSize': (6,10), 'timeRange':[100,900], 'plots': ['spectrogram'], 'showFig': True} -#simConfig.analysis['plotCSD'] = True #{'timeRange':[100,200]} +# cfg.analysis['plotCSD'] = {'saveFig': True, 'spacing_um': 150, 'overlay': 'CSD'} +# cfg.analysis['plotCSDTimeSeries'] = {'saveFig': True} +# cfg.analysis['plotCSDPSD'] = {'saveFig': True} diff --git a/examples/dipoleRecording/src/cfg_cell.py b/examples/dipoleRecording/src/cfg_cell.py index 6b9ee4ccc..f0e6f7526 100644 --- a/examples/dipoleRecording/src/cfg_cell.py +++ b/examples/dipoleRecording/src/cfg_cell.py @@ -16,7 +16,4 @@ cfg.analysis['plotTraces'] = {'include': [('E',0)], 'oneFigPer':'cell', 'overlay': True, 'figSize': (5,3),'saveFig': True} # Plot recorded traces for this list of cells #cfg.analysis['plotLFP'] = {'includeAxon': False, 'plots': ['timeSeries', 'locations'], 'figSize': (5,9), 'saveFig': True} -#cfg.analysis['getCSD'] = {'timeRange': [10,45],'spacing_um': 150, 'vaknin': True} -#cfg.analysis['plotCSD'] = {'timeRange': [10,45]} -#sim.analysis.getCSD(...args...) -#cfg.analysis['plotCSD'] = {} \ No newline at end of file +#cfg.analysis['plotCSD'] = {'timeRange': [10,45], 'saveFig': True} diff --git a/examples/dipoleRecording/src/init_cell.py b/examples/dipoleRecording/src/init_cell.py index e7d48cf61..a0fce582c 100644 --- a/examples/dipoleRecording/src/init_cell.py +++ b/examples/dipoleRecording/src/init_cell.py @@ -1,5 +1,5 @@ from netpyne import sim -cfg, netParams = sim.loadFromIndexFile('index_cell.npjson') +cfg, netParams = sim.loadFromIndexFile('index.npjson') sim.createSimulateAnalyze(netParams = netParams, simConfig = cfg) -#sim.analysis.plotCSD() \ No newline at end of file +#sim.analysis.plotCSD() diff --git a/netpyne/analysis/__init__.py b/netpyne/analysis/__init__.py index 203f5f44b..a719768f0 100644 --- a/netpyne/analysis/__init__.py +++ b/netpyne/analysis/__init__.py @@ -81,9 +81,7 @@ print('Warning: could not import interactive plotting functions; make sure the "bokeh" package is installed.') # Import CSD-related functions -# from .csd import getCSD, plotCSD from .csd import prepareCSD -# from .csd import prepareCSDPSD # Import dipole-related functions from .dipole import plotDipole, plotEEG diff --git a/netpyne/analysis/csd.py b/netpyne/analysis/csd.py index b314aea40..73f06ae9b 100644 --- a/netpyne/analysis/csd.py +++ b/netpyne/analysis/csd.py @@ -34,7 +34,7 @@ from .utils import exception, _saveFigData -def getbandpass( +def getBandpass( lfps, sampr, minf=0.05, @@ -73,7 +73,7 @@ def getbandpass( datband = np.array(datband) return datband -def Vaknin(x): +def vakninCorrection(x): """ Function to perform the Vaknin correction for CSD analysis @@ -105,7 +105,7 @@ def Vaknin(x): return x_new -def removemean(x, ax=1): +def removeMean(x, ax=1): """ Function to subtract the mean from an array or list @@ -238,6 +238,12 @@ def prepareCSD( if dt is None: dt = sim.cfg.recordStep + # slice data by timeRange, if relevant + if timeRange is None: + timeRange = [0, sim.cfg.duration] + else: + LFPData = LFPData[int(timeRange[0] / sim.cfg.recordStep) : int(timeRange[1] / sim.cfg.recordStep), :] + # Sampling rate of data recording during the simulation if sampr is None: # divide by 1000.0 to turn denominator from units of ms to s @@ -246,48 +252,29 @@ def prepareCSD( # Spacing between electrodes (in microns) if spacing_um is None: - spacing_um = sim.cfg.recordLFP[1][1] - sim.cfg.recordLFP[0][1] + # if not specified, use average spacing along y coord (depth) + yCoords = np.array(sim.cfg.recordLFP)[:,1] + spacing_um = (yCoords.max() - yCoords.min()) / (len(yCoords) - 1) # Convert spacing from microns to mm spacing_mm = spacing_um / 1000 - print('dt, sampr, spacing_um, spacing_mm values determined') - - - ## This retrieves: - # LFPData (as an array) - # dt --> recording time step (in ms) - # sampr --> sampling rate of data recording (in Hz) - # spacing_um --> spacing btwn electrodes (in um) - - #################################### - - # Bandpass filter the LFP data with getbandpass() fx defined above - datband = getbandpass(LFPData, sampr, minf, maxf) + # print('dt, sampr, spacing_um, spacing_mm values determined') - # Take CSD along smaller dimension - if datband.shape[0] > datband.shape[1]: - ax = 1 - else: - ax = 0 + # Bandpass filter the LFP data with getBandpass() fx defined above + datband = getBandpass(LFPData, sampr, minf, maxf) + # now each row is an electrode - `datband` shape is (N_electrodes, N_timesteps) # Vaknin correction if vaknin: - datband = Vaknin(datband) + datband = vakninCorrection(datband) # norm data if norm: - removemean(datband, ax=ax) - - # now each column (or row) is an electrode -- take CSD along electrodes - CSDData = -np.diff(datband, n=2, axis=ax) / spacing_mm**2 + removeMean(datband, ax=0) - # # Splice CSD data by timeRange, if relevant - # if timeRange is None: - # timeRange = [0, sim.cfg.duration] - # else: - # # lfpData = lfpData[int(timeRange[0]/sim.cfg.recordStep):int(timeRange[1]/sim.cfg.recordStep),:] - # CSDData = CSDData[:,int(timeRange[0]/sim.cfg.recordStep):int(timeRange[1]/sim.cfg.recordStep)] + # take CSD along electrodes dimension + CSDData = -np.diff(datband, n=2, axis=0) / spacing_mm**2 ##### SAVE DATA ####### # Add CSDData to sim.allSimData for later access @@ -298,249 +285,10 @@ def prepareCSD( sim.allSimData['CSDPops'] = {} sim.allSimData['CSDPops'][pop] = CSDData - # # create the output data dictionary - # data = {} - # data['electrodes'] = {} - # data['electrodes']['names'] = [] - # data['electrodes']['locs'] = [] - # data['electrodes']['csds'] = [] - - # # create an array of the time steps and store in output data - # t = np.arange(timeRange[0], timeRange[1], sim.cfg.recordStep) - # data['t'] = t - # return CSD_data or all data - if getAllData is True: + if getAllData: return CSDData, LFPData, sampr, spacing_um, dt - elif getAllData is False: - return CSDData - - -# def getbandpass(lfps, sampr, minf=0.05, maxf=300): -# """ -# Function to bandpass filter data - -# Parameters -# ---------- -# lfps : list or array -# LFP signal data arranged spatially in a column. -# **Default:** *required* - -# # electrode preparation (add average if needed) -# if 'all' in electrodes: -# electrodes.remove('all') -# electrodes.extend(list(range(int(sim.net.recXElectrode.nsites)))) - -# for i, elec in enumerate(electrodes): -# if elec == 'avg': -# csdSignal = np.mean(CSDData, axis=0) -# loc = None -# elif isinstance(elec, Number) and elec <= sim.net.recXElectrode.nsites: -# csdSignal = CSDData[elec,:] -# loc = sim.cfg.recordLFP[elec] - - -# data['electrodes']['names'].append(str(elec)) -# data['electrodes']['locs'].append(loc) -# data['electrodes']['csds'].append(csdSignal) ## <-- this can be turned into an array with np.array(data['electrodes']['csds'] -- NOTE: first row will be average -- data['electrodes']['csds'][0]) - -# ## testing line -# data['CSDData'] = CSDData -# ### NOTE: -# ### csd = np.array(data['electrodes']['csds']) -# ### csd = np.array(csd) -# ### csd[1:, :] == CSDData['CSDData'] ---> True, True, True... True - -# return data - - -# @exception -# def prepareCSDPSD( -# CSDData=None, -# sim=None, -# timeRange=None, -# electrodes=['avg', 'all'], -# pop=None, -# minFreq=1, -# maxFreq=100, -# stepFreq=1, -# normSignal=False, -# normPSD=False, -# transformMethod='morlet', -# **kwargs -# ): - -# """ -# Function to prepare data for plotting of power spectral density (PSD) of current source density (CSD) -# """ - -# <<<<<<< HEAD -# ======= -# datband = [] -# for i in range(len(lfps[0])): -# datband.append(bandpass(lfps[:, i], minf, maxf, df=sampr, zerophase=True)) -# >>>>>>> development - -# ## OLD ARGS --> -# # NFFT=256, -# # noverlap=128, -# # nperseg=256, -# # smooth=0, -# # logy=False, -# ## --> These were args in preparePSD for LFP but I bet these won't be relevant --> -# # filtFreq=False, -# # filtOrder=3, -# # detrend=False, - - -# <<<<<<< HEAD -# ======= - -# def Vaknin(x): -# """ -# Function to perform the Vaknin correction for CSD analysis -# >>>>>>> development - -# if not sim: -# from .. import sim - -# <<<<<<< HEAD -# ======= -# Parameters -# ---------- -# x : array -# Data to be corrected. -# **Default:** *required* -# >>>>>>> development - -# data = prepareCSD( -# sim=sim, -# electrodes=electrodes, -# timeRange=timeRange, -# pop=pop, -# **kwargs) - -# # prepareCSD args covered under kwargs --> -# # dt=dt, -# # sampr=sampr, -# # spacing_um=spacing_um, -# # minf=minf, -# # maxf=maxf, -# # vaknin=vaknin, -# # norm=norm, - -# print('Preparing CSD PSD data...') - - -# names = data['electrodes']['names'] -# csds = data['electrodes']['csds'] # KEEP IN MIND THAT THE FIRST [0] IS AVERAGE ELECTRODE!!! - -# allFreqs = [] -# allSignal = [] -# allNames = [] - - -# # Used in both transforms -# fs = int(1000.0/sim.cfg.recordStep) - - -# for index, csd in enumerate(csds): - -# # Morlet wavelet transform method -# if transformMethod == 'morlet': - -# from ..support.morlet import MorletSpec, index2ms -# morletSpec = MorletSpec(csd, fs, freqmin=minFreq, freqmax=maxFreq, freqstep=stepFreq) -# freqs = morletSpec.f -# spec = morletSpec.TFR -# signal = np.mean(spec, 1) -# ylabel = 'Power' - - -# allFreqs.append(freqs) -# allSignal.append(signal) -# allNames.append(names[index]) - -# if normPSD: -# vmax = np.max(allSignal) -# for index, signal in enumerate(allSignal): -# allSignal[index] = allSignal[index]/vmax - - -# psdFreqs = [] -# psdSignal = [] - -# for index, name in enumerate(names): -# freqs = allFreqs[index] -# signal = allSignal[index] - -# psdFreqs.append(freqs[freqs DEFINE FUNCTION prepareSpectrogram() ### -# # @exception -# # def prepareSpectrogram( -# # sim=None, -# # timeRange=None, -# # electrodes=['avg', 'all'], -# # pop=None, -# # CSDData=None, -# # minFreq=1, -# # maxFreq=100, -# # stepFreq=1, -# # normSignal=False, -# # normPSD=False, -# # normSpec=False, -# # transformMethod='morlet', -# # **kwargs): - -# # """ -# # Function to prepare data for plotting of the spectrogram -# # """ - - - -# <<<<<<< HEAD -# ======= -# # Preallocate array with 2 more rows than input array -# x_new = np.zeros((x.shape[0] + 2, x.shape[1])) -# >>>>>>> development - - - - -# <<<<<<< HEAD - -# ======= - -# def removemean(x, ax=1): -# """ -# Function to subtract the mean from an array or list - -# Parameters -# ---------- -# x : array -# Data to be processed. -# **Default:** *required* -# >>>>>>> development - - - - -# <<<<<<< HEAD - - -# ======= -# """ - -# mean = np.mean(x, axis=ax, keepdims=True) -# x -= mean -# >>>>>>> development + else: + from .lfp import prepareDataPerElectrode + CSDData = CSDData.T # to match the shape expected by prepareDataPerElectrode + return prepareDataPerElectrode(CSDData, electrodes, timeRange, sim) diff --git a/netpyne/analysis/csd_legacy.py b/netpyne/analysis/csd_legacy.py deleted file mode 100644 index 6c8f58eac..000000000 --- a/netpyne/analysis/csd_legacy.py +++ /dev/null @@ -1,651 +0,0 @@ -""" -Module with functions to extract and plot CSD info from LFP data - -""" - -from __future__ import print_function -from __future__ import division -from __future__ import unicode_literals -from __future__ import absolute_import - -from future import standard_library - -standard_library.install_aliases() - -try: - basestring -except NameError: - basestring = str - -import numpy as np -import scipy - -import matplotlib -from matplotlib import pyplot as plt -from matplotlib import ticker as ticker -import json -import sys -import os -from collections import OrderedDict -import warnings -from scipy.fftpack import hilbert -from scipy.signal import cheb2ord, cheby2, convolve, get_window, iirfilter, remez, decimate -from .filter import lowpass, bandpass -from .utils import exception, _saveFigData - - -def getbandpass(lfps, sampr, minf=0.05, maxf=300): - """ - Function to bandpass filter data - - Parameters - ---------- - lfps : list or array - LFP signal data arranged spatially in a column. - **Default:** *required* - - sampr : float - The data sampling rate. - **Default:** *required* - - minf : float - The high-pass filter frequency (Hz). - **Default:** ``0.05`` - - maxf : float - The low-pass filter frequency (Hz). - **Default:** ``300`` - - - Returns - ------- - data : array - The bandpass-filtered data. - - """ - - datband = [] - for i in range(len(lfps[0])): - datband.append(bandpass(lfps[:, i], minf, maxf, df=sampr, zerophase=True)) - - datband = np.array(datband) - - return datband - - -def Vaknin(x): - """ - Function to perform the Vaknin correction for CSD analysis - - Allows CSD to be performed on all N contacts instead of N-2 contacts (see Vaknin et al (1988) for more details). - - Parameters - ---------- - x : array - Data to be corrected. - **Default:** *required* - - - Returns - ------- - data : array - The corrected data. - - """ - - # Preallocate array with 2 more rows than input array - x_new = np.zeros((x.shape[0] + 2, x.shape[1])) - - # Duplicate first and last row of x into first and last row of x_new - x_new[0, :] = x[0, :] - x_new[-1, :] = x[-1, :] - - # Duplicate all of x into middle rows of x_neww - x_new[1:-1, :] = x - - return x_new - - -def removemean(x, ax=1): - """ - Function to subtract the mean from an array or list - - Parameters - ---------- - x : array - Data to be processed. - **Default:** *required* - - ax : int - The axis to remove the mean across. - **Default:** ``1`` - - - Returns - ------- - data : array - The processed data. - - """ - - mean = np.mean(x, axis=ax, keepdims=True) - x -= mean - - -# returns CSD in units of mV/mm**2 (assuming lfps are in mV) -def getCSD( - LFP_input_data=None, - LFP_input_file=None, - sampr=None, - dt=None, - spacing_um=None, - minf=0.05, - maxf=300, - norm=True, - vaknin=True, - save_to_sim=True, - getAllData=False, -): - """ - Function to extract CSD values from simulated LFP data - - Parameters - ---------- - LFP_input_data : list or numpy array - LFP data provided by user (mV). Each element of the list/array must be a list/array containing LFP data for an electrode. - **Default:** ``None`` pulls the data from the current NetPyNE sim object. - - LFP_input_file : str - Location of a JSON data file with LFP data. - **Default:** ``None`` - - sampr : float - Sampling rate for data recording (Hz). - **Default:** ``None`` uses ``1.0/sim.cfg.recordStep`` from the current NetPyNE sim object. - - dt : float - Time between recording points (ms). - **Default:** ``None`` uses ``sim.cfg.recordStep`` from the current NetPyNE sim object. - - spacing_um : float - Electrode contact spacing in units of microns. - **Default:** ``None`` pulls the information from the current NetPyNE sim object. If the data is empirical, defaults to ``100`` (microns). - - minf : float - Minimum frequency for bandpass filter (Hz). - **Default:** ``0.05`` - - maxf : float - Maximum frequency cutoff for bandpass filter (Hz). - **Default:** ``300`` - - norm : bool - Whether to subtract the mean from the signal or not. - **Default:** ``True`` subtracts the mean. - - vaknin : bool - Whether or not to to perform the Vaknin correction for CSD analysis. - **Default** ``True`` performs the correction. - - save_to_sim : bool - Whether to attempt to store CSD values in sim.allSimData, if this exists. - **Default** ``True`` attempts to store the data. - - getAllData : bool - True will have this function return dt, tt, timeRange, sampr, spacing_um, lfp_data, and CSD_data. - **Default** ``False`` returns only CSD_data. - - """ - - ### DEFAULT -- CONDITION 1 : LFP DATA COMES FROM SIMULATION ### - - # Get LFP data from simulation - if LFP_input_data is None and LFP_input_file is None: - - try: - from .. import sim - except: - raise Exception('No LFP input data, input file, or existing simulation. Cannot calculate CSD.') - - # time step used in simulation recording (in ms) - if dt is None: - dt = sim.cfg.recordStep - - # Get LFP data from sim and instantiate as a numpy array - sim_data_categories = sim.allSimData.keys() - - if 'LFP' in sim_data_categories: - lfp_data = np.array(sim.allSimData['LFP']) - else: - raise Exception('NO LFP DATA!! Need to re-run simulation with cfg.recordLFP enabled.') - - # Sampling rate of data recording during the simulation - if sampr is None: - # divide by 1000.0 to turn denominator from units of ms to s - sampr = 1.0 / (dt / 1000.0) # sim.cfg.recordStep --> == dt - - # Spacing between electrodes (in microns) - if spacing_um is None: - spacing_um = sim.cfg.recordLFP[1][1] - sim.cfg.recordLFP[0][1] - - ## This retrieves: - # lfp_data (as an array) - # dt --> recording time step (in ms) - # sampr --> sampling rate of data recording (in Hz) - # spacing_um --> spacing btwn electrodes (in um) - - ### CONDITION 2 : LFP DATA FROM SIM .JSON FILE ### # Note: need to expand capability to include a list of multiple files - - # load sim data from JSON file - elif LFP_input_data is None and '.json' in LFP_input_file: - - data = {} - with open(LFP_input_file) as file: - data['json_input_data'] = json.load(file) - - ## FOR MULTIPLE FILES - # for x in LFP_input_file: - # with open(x) as file: - # data[x] = json.load(file) - - # extract LFP data (only works in the 1 input file scenario; expand capability for multiple files) - for key in data.keys: - lfp_data_list = data[key]['simData']['LFP'] - - # cast LFP data as Numpy array - lfp_data = np.array(lfp_data_list) - - # get CSD data and relevant plotting params - csd_data = {} - for i in data.keys(): - csd_data[i] = {} - - if sampr is None: - csd_data[i]['sampr'] = 1.0 / ((data[i]['simConfig']['recordStep']) / 1000.0) - sampr = csd_data[i]['sampr'] - else: - csd_data[i]['sampr'] = sampr - - if spacing_um is None: - csd_data[i]['spacing_um'] = ( - data[i]['simConfig']['recordLFP'][1][1] - data[i]['simConfig']['recordLFP'][0][1] - ) - spacing_um = csd_data[i]['spacing_um'] - else: - csd_data[i]['spacing_um'] = spacing_um - - if dt is None: - csd_data[i]['dt'] = data[i]['simConfig']['recordStep'] - dt = csd_data[i]['dt'] - else: - csd_data[i]['dt'] = dt - - ## This retrieves: - # lfp_data (as a list) - # dt --> recording time step (in ms) - # sampr --> sampling rate of data recording (in Hz) - # spacing_um --> spacing btwn electrodes (in um) - - ### CONDITION 3 : ARBITRARY LFP DATA ### # NOTE: for condition 3 --> need to also retrieve the dt, sampr, and spacing_um !! - - # get lfp_data and cast as numpy array - elif len(LFP_input_data) > 0 and LFP_input_file is None: - lfp_data = np.array(LFP_input_data) - - #################################### - - # Convert spacing from microns to mm - spacing_mm = spacing_um / 1000 - - # Bandpass filter the LFP data with getbandpass() fx defined above - datband = getbandpass(lfp_data, sampr, minf, maxf) - - # Take CSD along smaller dimension - if datband.shape[0] > datband.shape[1]: - ax = 1 - else: - ax = 0 - print('ax = ' + str(ax)) - - # Vaknin correction - if vaknin: - datband = Vaknin(datband) - - # norm data - if norm: - removemean(datband, ax=ax) - - # now each column (or row) is an electrode -- take CSD along electrodes - CSD_data = -np.diff(datband, n=2, axis=ax) / spacing_mm**2 - - # noBandpass trial - datband_noBandpass = lfp_data.T - - if datband_noBandpass.shape[0] > datband_noBandpass.shape[1]: - ax = 1 - else: - ax = 0 - - if vaknin: - datband_noBandpass = Vaknin(datband_noBandpass) - - if norm: - removemean(datband_noBandpass, ax=ax) - - CSD_data_noBandpass = -np.diff(datband_noBandpass, n=2, axis=ax) / spacing_mm**2 - - # Add CSD and other param values to sim.allSimData for later access - if save_to_sim is True: - try: - from .. import sim - - sim.allSimData['CSD'] = {} - sim.allSimData['CSD']['sampr'] = sampr - sim.allSimData['CSD']['spacing_um'] = spacing_um - sim.allSimData['CSD']['CSD_data'] = CSD_data - sim.allSimData['CSD']['CSD_data_noBandpass'] = CSD_data_noBandpass - except: - print('NOTE: No sim.allSimData construct available to store CSD data.') - - # return CSD_data or all data - if getAllData is True: - return lfp_data, CSD_data, sampr, spacing_um, dt - if getAllData is False: - return CSD_data - - -# PLOTTING CSD - - -@exception -def plotCSD( - CSD_data=None, - LFP_input_data=None, - overlay=None, - timeRange=None, - sampr=None, - stim_start_time=None, - spacing_um=None, - ymax=None, - dt=None, - hlines=False, - layerLines=False, - layerBounds=None, - smooth=True, - fontSize=12, - figSize=(8, 8), - dpi=200, - saveFig=True, - showFig=True, -): - """ - Function to plot CSD values extracted from simulated LFP data - - Parameters - ---------- - CSD_data : list or array - CSD_data for plotting. - **Default:** ``None`` - - LFP_input_data : list or numpy array - LFP data provided by user (mV). Each element of the list/array must be a list/array containing LFP data for an electrode. - **Default:** ``None`` pulls the data from the current NetPyNE sim object. - - - overlay : str - Option to include LFP data overlaid on CSD color map plot. - **Default:** ``None`` provides no overlay - OPTIONS are 'LFP' or 'CSD' - - timeRange : list - Time range to plot [start, stop]. - **Default:** ``None`` plots entire time range - - sampr : float - Sampling rate for data recording (Hz). - **Default:** ``None`` uses ``1.0/sim.cfg.recordStep`` from the current NetPyNE sim object. - - stim_start_time : float - Time when stimulus is applied (ms). - **Default:** ``None`` does not add anything to plot. - **Options:** a float adds a vertical dashed line to the plot at stimulus onset. - - spacing_um : float - Electrode contact spacing in units of microns. - **Default:** ``None`` pulls the information from the current NetPyNE sim object. If the data is empirical, defaults to ``100`` (microns). - - ymax : float - The upper y-limit. - **Default:** ``None`` - - dt : float - Time between recording points (ms). - **Default:** ``None`` uses ``sim.cfg.recordStep`` from the current NetPyNE sim object. - - hlines : bool - Option to include horizontal lines on plot to indicate electrode positions. - **Default:** ``False`` - - layerLines : bool - Whether to plot horizontal lines over CSD plot at layer boundaries. - **Default:** ``False`` - - layerBounds : dict - Dictionary containing layer labels as keys, and layer boundaries as values, e.g. {'L1':100, 'L2': 160, 'L3': 950, 'L4': 1250, 'L5A': 1334, 'L5B': 1550, 'L6': 2000} - **Default:** ``None`` - - saveFig : bool or str - Whether and where to save the figure. - **Default:** ``True`` autosaves the figure. - **Options:** ``'/path/filename.ext'`` saves to a custom path and filename, valid file extensions are ``'.png'``, ``'.jpg'``, ``'.eps'``, and ``'.tiff'``. - - showFig : bool - Whether to show the figure. - **Default:** ``True`` - - """ - - print('Plotting CSD... ') - - # DEFAULT -- CONDITION 1 : GET CSD DATA FROM SIM - if CSD_data is None: - - from .. import sim - - LFP_data, CSD_data, sampr, spacing_um, dt = getCSD( - getAllData=True - ) # getCSD(sampr=sampr, spacing_um=spacing_um, dt=dt, getAllData=True) - - if timeRange is None: - timeRange = [0, sim.cfg.duration] - - tt = np.arange(timeRange[0], timeRange[1], dt) - - ymax = sim.cfg.recordLFP[-1][1] + spacing_um - - LFP_data = np.array(LFP_data)[int(timeRange[0] / dt) : int(timeRange[1] / dt), :] - CSD_data = CSD_data[:, int(timeRange[0] / dt) : int(timeRange[1] / dt)] - - CSD_data_noBandpass = sim.allSimData['CSD']['CSD_data_noBandpass'][ - :, int(timeRange[0] / sim.cfg.recordStep) : int(timeRange[1] / sim.cfg.recordStep) - ] - # CSD_data_noBandpass = CSD_data_noBandpass[:,int(timeRange[0]/dt):int(timeRange[1]/dt)] - - ### The problem with this setup is that it defaults to whatever was saved in .pkl !! - # sim_data_categories = sim.allSimData.keys() - - # if 'CSD' in sim_data_categories: - - # if timeRange is None: - # timeRange = [0,sim.cfg.duration] - - # dt = sim.cfg.recordStep - # tt = np.arange(timeRange[0],timeRange[1],dt) - - # spacing_um = sim.allSimData['CSD']['spacing_um'] - # #spacing_mm = spacing_um/1000 - - # ymax = sim.cfg.recordLFP[-1][1] + spacing_um - - # # get LFP data - # LFP_data = np.array(sim.allSimData['LFP'])[int(timeRange[0]/sim.cfg.recordStep):int(timeRange[1]/sim.cfg.recordStep),:] - - # # get CSD data - # CSD_data = sim.allSimData['CSD']['CSD_data'][:,int(timeRange[0]/sim.cfg.recordStep):int(timeRange[1]/sim.cfg.recordStep)] - - # # noBandpass trial - # CSD_data_noBandpass = sim.allSimData['CSD']['CSD_data_noBandpass'][:,int(timeRange[0]/sim.cfg.recordStep):int(timeRange[1]/sim.cfg.recordStep)] - - # else: - # raise Exception('No CSD data in sim.') - - # CONDITION 2 : ARBITRARY CSD DATA - elif CSD_data is not None: - if timeRange is None: - print('MUST PROVIDE TIME RANGE in ms') - else: - print('timeRange = ' + str(timeRange)) - - if dt is None: - print('MUST PROVIDE dt in ms') - else: - print('dt = ' + str(dt)) # batch0['simConfig']['recordStep'] - - if spacing_um is None: - print('MUST PROVIDE SPACING BETWEEN ELECTRODES in MICRONS') - else: - print('spacing_um = ' + str(spacing_um)) - - if ymax is None: - print('MUST PROVIDE YMAX (MAX DEPTH) in MICRONS') - else: - print('ymax = ' + str(ymax)) - - tt = np.arange(timeRange[0], timeRange[1], dt) - LFP_data = np.array(LFP_input_data)[int(timeRange[0] / dt) : int(timeRange[1] / dt), :] - - # PLOTTING - X = np.arange(timeRange[0], timeRange[1], dt) - Y = np.arange(CSD_data.shape[0]) - - # interpolation - CSD_spline = scipy.interpolate.RectBivariateSpline(Y, X, CSD_data) - Y_plot = np.linspace(0, CSD_data.shape[0], num=1000) - Z = CSD_spline(Y_plot, X) - - # plotting options - plt.rcParams.update({'font.size': fontSize}) - xmin = int(X[0]) - xmax = int(X[-1]) + 1 # int(sim.allSimData['t'][-1]) - ymin = 0 - extent_xy = [xmin, xmax, ymax, ymin] - - # set up figure - fig = plt.figure(figsize=figSize) - - # create plots w/ common axis labels and tick marks - axs = [] - numplots = 1 - gs_outer = matplotlib.gridspec.GridSpec( - 1, 1 - ) # (2, 2, figure=fig)#, wspace=0.4, hspace=0.2, height_ratios=[20, 1]) - - for i in range(numplots): - axs.append(plt.Subplot(fig, gs_outer[i * 2 : i * 2 + 2])) - fig.add_subplot(axs[i]) - axs[i].set_xlabel('Time (ms)', fontsize=fontSize) - axs[i].tick_params(axis='y', which='major', labelsize=fontSize) - axs[i].tick_params(axis='x', which='major', labelsize=fontSize) - - ## plot interpolated CSD color map - # if smooth: - # Z = scipy.ndimage.filters.gaussian_filter(Z, sigma = 5, mode='nearest')#smooth, mode='nearest') - - spline = axs[0].imshow( - Z, extent=extent_xy, interpolation='none', aspect='auto', origin='upper', cmap='jet_r', alpha=0.9 - ) - axs[0].set_ylabel('Contact depth (um)', fontsize=fontSize) - - # OVERLAY DATA ('LFP', 'CSD', or None) & Set title of plot - if overlay is None: - print('No data being overlaid') - axs[0].set_title('Current Source Density (CSD)', fontsize=fontSize) - - elif overlay is 'CSD' or overlay is 'LFP': - nrow = LFP_data.shape[1] - gs_inner = matplotlib.gridspec.GridSpecFromSubplotSpec( - nrow, 1, subplot_spec=gs_outer[0:2], wspace=0.0, hspace=0.0 - ) - subaxs = [] - - if overlay == 'CSD': - axs[0].set_title('CSD with time series overlay', fontsize=fontSize) - for chan in range(nrow): - subaxs.append(plt.Subplot(fig, gs_inner[chan], frameon=False)) - fig.add_subplot(subaxs[chan]) - subaxs[chan].margins(0.0, 0.01) - subaxs[chan].get_xaxis().set_visible(False) - subaxs[chan].get_yaxis().set_visible(False) - subaxs[chan].plot(X, CSD_data[chan, :], color='green', linewidth=0.3) # 'blue' - - elif overlay == 'LFP': - axs[0].set_title('CSD with LFP overlay', fontsize=fontSize) - for chan in range(nrow): - subaxs.append(plt.Subplot(fig, gs_inner[chan], frameon=False)) - fig.add_subplot(subaxs[chan]) - subaxs[chan].margins(0.0, 0.01) - subaxs[chan].get_xaxis().set_visible(False) - subaxs[chan].get_yaxis().set_visible(False) - subaxs[chan].plot(X, LFP_data[:, chan], color='gray', linewidth=0.3) - - else: - print('Invalid option specified for overlay argument -- no data overlaid') - axs[0].set_title('Current Source Density (CSD)', fontsize=fontSize) - - # add horizontal lines at electrode locations - if hlines: - for i in range(len(sim.cfg.recordLFP)): - axs[0].hlines(sim.cfg.recordLFP[i][1], xmin, xmax, colors='pink', linewidth=1, linestyles='dashed') - - if layerLines: - if layerBounds is None: - print('No layer boundaries given -- will not overlay layer boundaries on CSD plot') - else: - layerKeys = [] - for i in layerBounds.keys(): - axs[0].hlines(layerBounds[i], xmin, xmax, colors='black', linewidth=1, linestyles='dotted') - layerKeys.append(i) # makes a list with names of each layer, as specified in layerBounds dict argument - - for n in range(len(layerKeys)): # label the horizontal layer lines with the proper layer label - if n == 0: - axs[0].text( - xmax + 5, layerBounds[layerKeys[n]] / 2, layerKeys[n], color='black', fontsize=fontSize - ) - else: - axs[0].text( - xmax + 5, - (layerBounds[layerKeys[n]] + layerBounds[layerKeys[n - 1]]) / 2, - layerKeys[n], - color='black', - fontsize=fontSize, - verticalalignment='center', - ) - - # set vertical line at stimulus onset - if type(stim_start_time) is int or type(stim_start_time) is float: - axs[0].vlines(stim_start_time, ymin, ymax, colors='red', linewidth=1, linestyles='dashed') - - # save figure - if saveFig: - if isinstance(saveFig, basestring): - filename = saveFig - else: - filename = sim.cfg.filename + '_CSD.png' - try: - plt.savefig(filename, dpi=dpi) - except: - plt.savefig('CSD_fig.png', dpi=dpi) - - # display figure - if showFig: - plt.show() diff --git a/netpyne/analysis/lfp.py b/netpyne/analysis/lfp.py index da514d447..56dd2b7e6 100644 --- a/netpyne/analysis/lfp.py +++ b/netpyne/analysis/lfp.py @@ -38,7 +38,6 @@ def prepareLFP( electrodes=['avg', 'all'], pop=None, LFPData=None, - logy=False, normSignal=False, filtFreq=False, filtOrder=3, @@ -55,34 +54,20 @@ def prepareLFP( if not sim: from .. import sim - # create the output data dictionary - data = {} - data['electrodes'] = {} - data['electrodes']['names'] = [] - data['electrodes']['locs'] = [] - data['electrodes']['lfps'] = [] - # set time range if timeRange is None: timeRange = [0, sim.cfg.duration] - # create an array of the time steps - t = np.arange(timeRange[0], timeRange[1], sim.cfg.recordStep) - data['t'] = t - # accept input lfp data if LFPData is not None: # loading LFPData is not yet functional - lfp = LFPData[int(timeRange[0] / sim.cfg.recordStep) : int(timeRange[1] / sim.cfg.recordStep), :] + lfp = LFPData else: if pop and pop in sim.allSimData['LFPPops']: - lfp = np.array(sim.allSimData['LFPPops'][pop])[ - int(timeRange[0] / sim.cfg.recordStep) : int(timeRange[1] / sim.cfg.recordStep), : - ] + lfp = np.array(sim.allSimData['LFPPops'][pop]) else: - lfp = np.array(sim.allSimData['LFP'])[ - int(timeRange[0] / sim.cfg.recordStep) : int(timeRange[1] / sim.cfg.recordStep), : - ] + lfp = np.array(sim.allSimData['LFP']) + lfp = lfp[int(timeRange[0] / sim.cfg.recordStep) : int(timeRange[1] / sim.cfg.recordStep), :] # filter the signals if filtFreq: @@ -115,52 +100,56 @@ def prepareLFP( lfp[:, i] /= max(lfp[:, i]) # electrode preparation + data = prepareDataPerElectrode(lfp, electrodes, timeRange, sim) + + # data['electrodes']['lfps'] = np.transpose(np.array(data['electrodes']['lfps'])) + + return data + +def prepareDataPerElectrode(signalPerElectrode, electrodes, timeRange, sim): + + # create the output data dictionary + data = {} + data['electrodes'] = {} + data['electrodes']['names'] = [] + data['electrodes']['locs'] = [] + data['electrodes']['data'] = [] + + # create an array of the time steps + t = np.arange(timeRange[0], timeRange[1], sim.cfg.recordStep) + data['t'] = t + if 'all' in electrodes: electrodes.remove('all') electrodes.extend(list(range(int(sim.net.recXElectrode.nsites)))) - # if 'avg' in electrodes: - # electrodes.remove('avg') - # data['electrodes']['names'].append('avg') - # data['electrodes']['locs'].append(None) - # data['electrodes']['lfps'].append(np.mean(lfp, axis=1)) - for i, elec in enumerate(electrodes): - if isinstance(elec, Number) and (LFPData is not None or elec <= sim.net.recXElectrode.nsites): - lfpSignal = lfp[:, elec] + loc = None + if isinstance(elec, Number) and (elec <= sim.net.recXElectrode.nsites): + signal = signalPerElectrode[:, elec] loc = sim.cfg.recordLFP[elec] elif elec == 'avg': - lfpSignal = np.mean(lfp, axis=1) - loc = None - elif isinstance(elec, list) and ( - LFPData is not None or all([x <= sim.net.recXElectrode.nsites for x in elec]) - ): - lfpSignal = np.mean(lfp[:, elec], axis=1) - loc = None - - if len(t) < len(lfpSignal): - lfpSignal = lfpSignal[: len(t)] + signal = np.mean(signalPerElectrode, axis=1) + elif isinstance(elec, list) and (all([x <= sim.net.recXElectrode.nsites for x in elec])): + signal = np.mean(signalPerElectrode[:, elec], axis=1) data['electrodes']['names'].append(str(elec)) data['electrodes']['locs'].append(loc) - data['electrodes']['lfps'].append(lfpSignal) - - # data['electrodes']['lfps'] = np.transpose(np.array(data['electrodes']['lfps'])) - + data['electrodes']['data'].append(signal) return data @exception def preparePSD( LFPData=None, + CSD=False, sim=None, timeRange=None, electrodes=['avg', 'all'], pop=None, NFFT=256, noverlap=128, - nperseg=256, minFreq=1, maxFreq=100, stepFreq=1, @@ -181,24 +170,35 @@ def preparePSD( if not sim: from .. import sim - data = prepareLFP( - sim=sim, - timeRange=timeRange, - electrodes=electrodes, - pop=pop, - LFPData=LFPData, - logy=logy, - normSignal=normSignal, - filtFreq=filtFreq, - filtOrder=filtOrder, - detrend=detrend, - **kwargs - ) + if not CSD: + data = prepareLFP( + sim=sim, + timeRange=timeRange, + electrodes=electrodes, + pop=pop, + LFPData=LFPData, + logy=logy, + normSignal=normSignal, + filtFreq=filtFreq, + filtOrder=filtOrder, + detrend=detrend, + **kwargs + ) + else: + data = sim.analysis.prepareCSD( + sim=sim, + timeRange=timeRange, + electrodes=electrodes, + pop=pop, + getAllData=False, + **kwargs + ) + print('Preparing PSD data...') names = data['electrodes']['names'] - lfps = data['electrodes']['lfps'] + lfps = data['electrodes']['data'] allFreqs = [] allSignal = [] @@ -331,7 +331,7 @@ def prepareSpectrogram( if not timeRange: timeRange = [0, sim.cfg.duration] - lfps = np.array(data['electrodes']['lfps']) + lfps = np.array(data['electrodes']['data']) names = data['electrodes']['names'] electrodes = data['electrodes'] diff --git a/netpyne/analysis/tools.py b/netpyne/analysis/tools.py index b288f02fd..9d68db49f 100644 --- a/netpyne/analysis/tools.py +++ b/netpyne/analysis/tools.py @@ -256,10 +256,10 @@ def plotData(sim=None): if sim.timingData['totalTime'] <= 1.2 * sumTime: # Print total time (only if makes sense) print(('\nTotal time = %0.2f s' % sim.timingData['totalTime'])) - try: - print('\nEnd time: ', datetime.now()) - except: - pass + # try: + # print('\nEnd time: ', datetime.now()) + # except: + # pass def saveData(data, fileName=None, fileDesc=None, fileType=None, fileDir=None, sim=None, **kwargs): diff --git a/netpyne/analysis/wrapper.py b/netpyne/analysis/wrapper.py index c2e4bfc79..701b0ffd2 100644 --- a/netpyne/analysis/wrapper.py +++ b/netpyne/analysis/wrapper.py @@ -20,7 +20,7 @@ # ------------------------------------------------------------------------------------------------------------------- ## Wrapper to run analysis functions in simConfig # ------------------------------------------------------------------------------------------------------------------- -def plotData(): +def plotData(): # TODO: remove? """ Function for/to diff --git a/netpyne/plotting/__init__.py b/netpyne/plotting/__init__.py index 3a1e06a2f..3576f367d 100644 --- a/netpyne/plotting/__init__.py +++ b/netpyne/plotting/__init__.py @@ -19,17 +19,13 @@ from .plotRaster import plotRaster from .plotSpikeHist import plotSpikeHist from .plotSpikeFreq import plotSpikeFreq -from .plotLFPTimeSeries import plotLFPTimeSeries -from .plotLFPPSD import plotLFPPSD +from .plotTimeSeries import plotLFPTimeSeries, plotCSDTimeSeries +from .plotTimeSeriesPSD import plotLFPPSD, plotCSDPSD from .plotLFPSpectrogram import plotLFPSpectrogram from .plotLFPLocations import plotLFPLocations from .plotShape import plotShape from .plotCSD import plotCSD -# # NEW CSD PLOTTING FUNCTIONS TESTS!! -# from .plotCSDTimeSeries import plotCSDTimeSeries -# from .plotCSDPSD import plotCSDPSD - """ # ------------------------------------------------------------------------------------------------------------------- diff --git a/netpyne/plotting/plotCSD.py b/netpyne/plotting/plotCSD.py index 2921afbae..b2ddae562 100644 --- a/netpyne/plotting/plotCSD.py +++ b/netpyne/plotting/plotCSD.py @@ -16,7 +16,6 @@ def plotCSD( dt=None, sampr=None, spacing_um=None, - norm=True, fontSize=12, ymax=None, figSize=(8, 8), @@ -110,6 +109,10 @@ def plotCSD( Whether or not to plot the smoothed interpoloation **Default:** ``True`` + colorbar : bool + Whetehr or not to plot the colorbar + **Default:** ``True`` + """ # If there is no input data, get the data from the NetPyNE sim object @@ -119,17 +122,20 @@ def plotCSD( else: sim = kwargs['sim'] - CSDData, LFPData, sampr, spacing_um, dt = sim.analysis.prepareCSD(sim=sim, pop=pop, dt=dt, sampr=sampr, spacing_um=spacing_um, getAllData=True, **kwargs) + CSDData, LFPData, sampr, spacing_um, dt = sim.analysis.prepareCSD( + sim=sim, + timeRange=timeRange, + pop=pop, + dt=dt, + sampr=sampr, + spacing_um=spacing_um, + getAllData=True, + **kwargs) + else: + pass # TODO: ensure time slicing works properly in case CSDData is passed as an argument if timeRange is None: timeRange = [0, sim.cfg.duration] - else: - LFPData = np.array(LFPData)[ - int(timeRange[0] / dt) : int(timeRange[1] / dt), : - ] # NOTE: THIS SHOULD ALREADY BE AN ARRAY - CSDData = CSDData[:, int(timeRange[0] / dt) : int(timeRange[1] / dt)] - - tt = np.arange(timeRange[0], timeRange[1], dt) ##################################################### print('Plotting CSD... ') @@ -197,7 +203,7 @@ def plotCSD( print('No overlay') axs[0].set_title(csdTitle, fontsize=fontSize) - elif overlay is 'CSD' or overlay is 'LFP': + elif overlay == 'CSD' or overlay == 'LFP': nrow = LFPData.shape[1] if colorbar: gs_inner = matplotlib.gridspec.GridSpecFromSubplotSpec( @@ -241,7 +247,7 @@ def plotCSD( legendLabel = False else: - print('Invalid option specified for overlay argument -- no data overlaid') + print(f'Invalid option specified for overlay argument ({overlay}) -- no data overlaid') axs[0].set_title('Current Source Density (CSD)', fontsize=fontSize) # add horizontal lines at electrode locations @@ -308,4 +314,4 @@ def plotCSD( _showFigure() #plt.show() - return fig, axs + return fig, axs diff --git a/netpyne/plotting/plotCSDPSD.py b/netpyne/plotting/plotCSDPSD.py deleted file mode 100644 index d700b82a4..000000000 --- a/netpyne/plotting/plotCSDPSD.py +++ /dev/null @@ -1,252 +0,0 @@ -# Generate plots of CSD PSD and related analyses - - -from ..analysis.utils import exception -import numpy as np -import math -from .plotter import LinesPlotter -from .plotter import MultiPlotter - - -@exception -def plotCSDPSD( - PSDData=None, - axis=None, - timeRange=None, - electrodes=['avg', 'all'], - pop=None, - separation=1.0, - roundOffset=True, - minFreq=1, - maxFreq=100, - stepFreq=1, - normSignal=False, - normPSD=False, - transformMethod='morlet', - orderInverse=True, - legend=True, - colorList=None, - returnPlotter=False, - **kwargs): - - """Function to produce a plot of CSD Power Spectral Density (PSD) data - - NetPyNE Options - --------------- - sim : NetPyNE sim object - The *sim object* from which to get data. - - *Default:* ``None`` uses the current NetPyNE sim object - - Parameters - ---------- - - axis : matplotlib axis - The axis to plot into, allowing overlaying of plots. - - *Default:* ``None`` produces a new figure and axis. - - separation : float - Use to increase or decrease distance between signals on the plot. - - *Default:* ``1.0`` - - roundOffset : bool - Attempts to line up PSD signals with gridlines - - *Default:* ``True`` - - orderInverse : bool - Whether to invert the order of plotting. - - *Default:* ``True`` - - legend : bool - Whether or not to add a legend to the plot. - - *Default:* ``True`` adds a legend. - - colorList : list - A *list* of colors to draw from when plotting. - - *Default:* ``None`` uses the default NetPyNE colorList. - - returnPlotter : bool - Whether to return the figure or the NetPyNE MetaFig object. - - *Default:* ``False`` returns the figure. - - Returns - ------- - CSDPSDPlot : *matplotlib figure* - By default, returns the *figure*. If ``returnPlotter`` is ``True``, instead returns the NetPyNE MetaFig. - """ - - - # If there is no input data, get the data from the NetPyNE sim object - if PSDData is None: - if 'sim' not in kwargs: - from .. import sim - else: - sim = kwargs['sim'] - - PSDData = sim.analysis.prepareCSDPSD( - CSDData=None, - sim=sim, - timeRange=timeRange, - electrodes=electrodes, - pop=pop, - minFreq=minFreq, - maxFreq=maxFreq, - stepFreq=stepFreq, - normSignal=normSignal, - normPSD=normPSD, - transformMethod=transformMethod, - **kwargs - ) - - # CSDData=None, - # sim=None, - # timeRange=None, - # electrodes=['avg', 'all'], - # pop=None, - # minFreq=1, - # maxFreq=100, - # stepFreq=1, - # normSignal=False, - # normPSD=False, - # transformMethod='morlet', - # **kwargs - # ): - - print('Plotting CSD power spectral density (PSD)...') - - # If input is a dictionary, pull the data out of it - if type(PSDData) == dict: - - freqs = PSDData['psdFreqs'] - freq = freqs[0] - psds = PSDData['psdSignal'] - names = PSDData['psdNames'] - colors = PSDData.get('colors') - linewidths = PSDData.get('linewidths') - alphas = PSDData.get('alphas') - axisArgs = PSDData.get('axisArgs') - - - # Set up colors, linewidths, and alphas for the plots - if not colors: - if not colorList: - from .plotter import colorList - colors = colorList[0:len(psds)] - - if not linewidths: - linewidths = [1.0 for name in names] - - if not alphas: - alphas = [1.0 for name in names] - - # Create a dictionary to hold axis inputs - if not axisArgs: - axisArgs = {} - title = 'CSD Power Spectral Density' - if pop: - title += ' - Population: ' + pop - axisArgs['title'] = title - axisArgs['xlabel'] = 'Frequency (Hz)' - axisArgs['ylabel'] = 'Power (mVˆ2 / Hz) -- NOTE: CORRECT??' ## ensure this is correct? - - - if axis != 'multi': - axisArgs['grid'] = {'which': 'both'} - - - # Link colors to traces, make avg plot black, add separation to traces - plotColors = [] - legendLabels = [] - colorIndex = 0 - offset = np.absolute(psds).max() * separation - - if axis == 'multi': - offset = 0 - roundOffset = False - - if roundOffset: - sigfigs = 1 - if type(roundOffset) == int: - sigfigs = roundOffset - offset = round(offset, sigfigs - int(math.floor(math.log10(abs(offset)))) - 1) - - for index, (name, psd) in enumerate(zip(names, psds)): - legendLabels.append(name) - if orderInverse: - psds[index] = index * offset - psd - axisArgs['invert_yaxis'] = True - else: - psds[index] = index * offset + psd - if name == 'avg': - plotColors.append('black') - else: - plotColors.append(colors[colorIndex]) - colorIndex += 1 - - - # Create a dictionary with the inputs for a line plot - linesData = {} - linesData['x'] = freq - linesData['y'] = psds - linesData['color'] = plotColors - linesData['marker'] = None - linesData['markersize'] = None - linesData['linewidth'] = None - linesData['alpha'] = None - linesData['label'] = legendLabels - - # If a kwarg matches a lines input key, use the kwarg value instead of the default - for kwarg in list(kwargs.keys()): - if kwarg in linesData: - linesData[kwarg] = kwargs[kwarg] - kwargs.pop(kwarg) - - # create Plotter object - if axis != 'multi': - plotter = LinesPlotter(data=linesData, kind='CSDPSD', axis=axis, **axisArgs, **kwargs) - else: - plotter = MultiPlotter(data=linesData, kind='CSDPSD', metaFig=None, **axisArgs, **kwargs) - ### NOTE: only kind='LFPPSD' exists for MultiPlotter in plotter.py --> ?? - - - metaFig = plotter.metafig - - # Set up the default legend settings - legendKwargs = {} - legendKwargs['title'] = 'Electrodes' - legendKwargs['loc'] = 'upper right' - legendKwargs['fontsize'] = 'small' - - # add the legend - if legend: - axisArgs['legend'] = legendKwargs - - # Generate the figure - PSDPlot = plotter.plot(**axisArgs, **kwargs) - - - # Default is to return the figure, but you can also return the plotter - if returnPlotter: - return metaFig - else: - return PSDPlot - - - - - - - - - - - - - diff --git a/netpyne/plotting/plotCSDTimeSeries.py b/netpyne/plotting/plotCSDTimeSeries.py deleted file mode 100644 index 7aa22642d..000000000 --- a/netpyne/plotting/plotCSDTimeSeries.py +++ /dev/null @@ -1,273 +0,0 @@ -# Generate plots of CSD (current source density) and related analyses - - -from ..analysis.utils import exception -import numpy as np -from .plotter import LinesPlotter - -@exception -def plotCSDTimeSeries( - CSDData=None, - axis=None, - timeRange=None, - electrodes=['avg', 'all'], - pop=None, - separation=1.0, - orderInverse=True, - overlay=False, - scalebar=True, - legend=True, - colorList=None, - returnPlotter=False, - **kwargs): - - """" - NetPyNE Options - --------------- - sim : NetPyNE sim object - The *sim object* from which to get data. - - *Default:* ``None`` uses the current NetPyNE sim object - - - Parameters - ---------- - CSDData : dict, str - The data necessary to plot the CSD signals. - - *Default:* ``None`` uses ``analysis.prepareCSD`` to produce ``CSDData`` using the current NetPyNE sim object. - - If a *str* it must represent a file path to previously saved data. - - axis : matplotlib axis - The axis to plot into, allowing overlaying of plots. - - *Default:* ``None`` produces a new figure and axis. - - timeRange : list - Time range to include in the raster: ``[min, max]``. - - *Default:* ``None`` uses the entire simulation - - electrodes : list - A *list* of the electrodes to plot from. - - *Default:* ``['avg', 'all']`` plots each electrode as well as their average - - pop : str - A population name to calculate signals from. - - *Default:* ``None`` uses all populations. - - separation : float - Use to increase or decrease distance between signals on the plot. - - *Default:* ``1.0`` - - orderInverse : bool - Whether to invert the order of plotting. - - *Default:* ``True`` - - overlay : bool - Option to label signals with a color-matched overlay. - - *Default:* ``False`` - - scalebar : bool - Whether to to add a scalebar to the plot. - - *Default:* ``True`` - - legend : bool - Whether or not to add a legend to the plot. - - *Default:* ``True`` adds a legend. - - colorList : list - A *list* of colors to draw from when plotting. - - *Default:* ``None`` uses the default NetPyNE colorList. - - returnPlotter : bool - Whether to return the figure or the NetPyNE MetaFig object. - - *Default:* ``False`` returns the figure. - """ - - - - """Function to produce a line plot of CSD electrode signals""" - - - - - # If there is no input data, get the data from the NetPyNE sim object - if CSDData is None: - if 'sim' not in kwargs: - from .. import sim - else: - sim = kwargs['sim'] - - CSDData = sim.analysis.prepareCSD( - sim=sim, - timeRange=timeRange, - electrodes=electrodes, - pop=pop, - **kwargs) - - ### ARGS THAT WILL PROBABLY / SHOULD BE COVERED UNDER KWARGS --> - # dt=None, - # sampr=None, - # spacing_um=None, - # minf=0.05, - # maxf=300, - # vaknin=True, - # norm=False, - - - - print('Plotting CSD time series...') - - - # If input is a dictionary, pull the data out of it - if type(CSDData) == dict: - - t = CSDData['t'] - csds = CSDData['electrodes']['csds'] - names = CSDData['electrodes']['names'] - locs = CSDData['electrodes']['locs'] - colors = CSDData.get('colors') - linewidths = CSDData.get('linewidths') - alphas = CSDData.get('alphas') - axisArgs = CSDData.get('axisArgs') - - - # Set up colors, linewidths, and alphas for the plots - if not colors: - if not colorList: - from .plotter import colorList - colors = colorList[0:len(csds)] - - if not linewidths: - linewidths = [1.0 for csd in csds] - - if not alphas: - alphas = [1.0 for csd in csds] - - - - # Create a dictionary to hold axis inputs - if not axisArgs: - axisArgs = {} - title = 'CSD Time Series Plot' - if pop: - title += ' - Population: ' + pop - axisArgs['title'] = title - axisArgs['xlabel'] = 'Time (ms)' - axisArgs['ylabel'] = 'CSD Signal (mV/(mm^2))' - - - # Link colors to traces, make avg plot black, add separation to traces - plotColors = [] - legendLabels = [] - colorIndex = 0 - offset = np.absolute(csds).max() * separation - - for index, (name, csd) in enumerate(zip(names, csds)): - legendLabels.append(name) - csds[index] = index * offset + csd - if orderInverse: - csds[index] = index * offset - csd - axisArgs['invert_yaxis'] = True - else: - csds[index] = index * offset + csd - if name == 'avg': - plotColors.append('black') - else: - plotColors.append(colors[colorIndex]) - colorIndex += 1 - - - # Create a dictionary with the inputs for a line plot - linesData = {} - linesData['x'] = t - linesData['y'] = csds - linesData['color'] = plotColors - linesData['marker'] = None - linesData['markersize'] = None - linesData['linewidth'] = linewidths - linesData['alpha'] = alphas - linesData['label'] = legendLabels - - - # If a kwarg matches a lines input key, use the kwarg value instead of the default - for kwarg in list(kwargs.keys()): - if kwarg in linesData: - linesData[kwarg] = kwargs[kwarg] - kwargs.pop(kwarg) - - # create Plotter object - linesPlotter = LinesPlotter(data=linesData, kind='CSDTimeSeries', axis=axis, **axisArgs, **kwargs) - metaFig = linesPlotter.metafig - - - # Set up the default legend settings - legendKwargs = {} - legendKwargs['title'] = 'Electrodes' - legendKwargs['loc'] = 'upper right' - legendKwargs['fontsize'] = 'small' - - - # add the legend - if legend: - axisArgs['legend'] = legendKwargs - - # add the scalebar - if scalebar: - args = {} - args['hidey'] = True - args['matchy'] = True - args['hidex'] = False - args['matchx'] = False - args['sizex'] = 0 - args['sizey'] = 1.0 - args['ymax'] = 0.25 * offset - args['unitsy'] = 'mm - FIX' #'$\mu$V' - args['scaley'] = 1000.0 - args['loc'] = 3 - args['pad'] = 0.5 - args['borderpad'] = 0.5 - args['sep'] = 3 - args['prop'] = None - args['barcolor'] = "black" - args['barwidth'] = 2 - args['space'] = 0.25 * offset - - axisArgs['scalebar'] = args - - - if overlay: - kwargs['tightLayout'] = False - for index, name in enumerate(names): - linesPlotter.axis.text(t[0]-0.07*(t[-1]-t[0]), (index*offset), name, color=plotColors[index], ha='center', va='center', fontsize='large', fontweight='bold') - linesPlotter.axis.spines['top'].set_visible(False) - linesPlotter.axis.spines['right'].set_visible(False) - linesPlotter.axis.spines['left'].set_visible(False) - if len(names) > 1: - linesPlotter.fig.text(0.05, 0.4, 'CSD electrode', color='k', ha='left', va='bottom', fontsize='large', rotation=90) - - - # Generate the figure - CSDTimeSeriesPlot = linesPlotter.plot(**axisArgs, **kwargs) - - - # Default is to return the figure, but you can also return the plotter - if returnPlotter: - return metaFig - else: - return CSDTimeSeriesPlot - - - - diff --git a/netpyne/plotting/plotLFPTimeSeries.py b/netpyne/plotting/plotTimeSeries.py similarity index 52% rename from netpyne/plotting/plotLFPTimeSeries.py rename to netpyne/plotting/plotTimeSeries.py index 40a58f7ac..2285ddd2d 100644 --- a/netpyne/plotting/plotLFPTimeSeries.py +++ b/netpyne/plotting/plotTimeSeries.py @@ -10,7 +10,6 @@ from .plotter import MetaFigure import numpy as np - @exception def plotLFPTimeSeries( LFPData=None, @@ -19,7 +18,6 @@ def plotLFPTimeSeries( electrodes=['avg', 'all'], pop=None, separation=1.0, - logy=False, normSignal=False, filtFreq=False, filtOrder=3, @@ -34,13 +32,6 @@ def plotLFPTimeSeries( ): """Function to produce a line plot of LFP electrode signals - NetPyNE Options - --------------- - sim : NetPyNE sim object - The *sim object* from which to get data. - - *Default:* ``None`` uses the current NetPyNE sim object - Parameters ---------- LFPData : dict, str @@ -75,11 +66,6 @@ def plotLFPTimeSeries( *Default:* ``1.0`` - logy : bool - Whether to use a log axis. - - *Default:* ``False`` - normSignal : bool Whether to normalize the data. @@ -178,8 +164,7 @@ def plotLFPTimeSeries( By default, returns the *figure*. If ``returnPlotter`` is ``True``, instead returns the NetPyNE MetaFig. """ - - # If there is no input data, get the data from the NetPyNE sim object + # If there is no input data, get the data from the NetPyNE sim object if LFPData is None: if 'sim' not in kwargs: from .. import sim @@ -192,7 +177,6 @@ def plotLFPTimeSeries( electrodes=electrodes, pop=pop, LFPData=LFPData, - logy=logy, normSignal=normSignal, filtFreq=filtFreq, filtOrder=filtOrder, @@ -202,17 +186,321 @@ def plotLFPTimeSeries( print('Plotting LFP time series...') + return plotTimeSeries( + data=LFPData, + label="LFPTimeSeries", + title='LFP Time Series Plot', + yLabel='LFP Signal (uV)', + axis=axis, + pop=pop, + separation=separation, + orderInverse=orderInverse, + overlay=overlay, + scalebar=scalebar, + legend=legend, + colorList=colorList, + returnPlotter=returnPlotter, + **kwargs) + + + +@exception +def plotCSDTimeSeries( + CSDData=None, + axis=None, + timeRange=None, + electrodes=['avg', 'all'], + pop=None, + separation=1.0, + orderInverse=True, + overlay=False, + scalebar=True, + legend=True, + colorList=None, + returnPlotter=False, + **kwargs +): + """" + + Parameters + ---------- + CSDData : dict, str + The data necessary to plot the CSD signals. + + *Default:* ``None`` uses ``analysis.prepareCSD`` to produce ``CSDData`` using the current NetPyNE sim object. + + If a *str* it must represent a file path to previously saved data. + + axis : matplotlib axis + The axis to plot into, allowing overlaying of plots. + + *Default:* ``None`` produces a new figure and axis. + + timeRange : list + Time range to include in the raster: ``[min, max]``. + + *Default:* ``None`` uses the entire simulation + + electrodes : list + A *list* of the electrodes to plot from. + + *Default:* ``['avg', 'all']`` plots each electrode as well as their average + + pop : str + A population name to calculate signals from. + + *Default:* ``None`` uses all populations. + + separation : float + Use to increase or decrease distance between signals on the plot. + + *Default:* ``1.0`` + + orderInverse : bool + Whether to invert the order of plotting. + + *Default:* ``True`` + + overlay : bool + Option to label signals with a color-matched overlay. + + *Default:* ``False`` + + scalebar : bool + Whether to to add a scalebar to the plot. + + *Default:* ``True`` + + legend : bool + Whether or not to add a legend to the plot. + + *Default:* ``True`` adds a legend. + + colorList : list + A *list* of colors to draw from when plotting. + + *Default:* ``None`` uses the default NetPyNE colorList. + + returnPlotter : bool + Whether to return the figure or the NetPyNE MetaFig object. + + *Default:* ``False`` returns the figure. + + + Plot Options + ------------ + showFig : bool + Whether to show the figure. + + *Default:* ``False`` + + saveFig : bool + Whether to save the figure. + + *Default:* ``False`` + + overwrite : bool + whether to overwrite existing figure files. + + *Default:* ``True`` overwrites the figure file + + *Options:* ``False`` adds a number to the file name to prevent overwriting + + legendKwargs : dict + a *dict* containing any or all legend kwargs. These include ``'title'``, ``'loc'``, ``'fontsize'``, ``'bbox_to_anchor'``, ``'borderaxespad'``, and ``'handlelength'``. + + rcParams : dict + a *dict* containing any or all matplotlib rcParams. To see all options, execute ``import matplotlib; print(matplotlib.rcParams)`` in Python. Any options in this *dict* will be used for this current figure and then returned to their prior settings. + + title : str + the axis title + + xlabel : str + label for x-axis + + ylabel : str + label for y-axis + + linewidth : int + line width + + alpha : float + line opacity (0-1) + + + Returns + ------- + LFPTimeSeriesPlot : *matplotlib figure* + By default, returns the *figure*. If ``returnPlotter`` is ``True``, instead returns the NetPyNE MetaFig. + + """ + + """Function to produce a line plot of CSD electrode signals""" + + # If there is no input data, get the data from the NetPyNE sim object + if CSDData is None: + if 'sim' not in kwargs: + from .. import sim + else: + sim = kwargs['sim'] + + CSDData = sim.analysis.prepareCSD( + sim=sim, + timeRange=timeRange, + electrodes=electrodes, + pop=pop, + getAllData=False, + **kwargs) + + print('Plotting CSD time series...') + + from .plotTimeSeries import plotTimeSeries + return plotTimeSeries( + data=CSDData, + label='CSDTimeSeries', + title='CSD Time Series Plot', + yLabel=r'CSD Signal (mV/(mm$^2$))', + axis=axis, + pop=pop, + separation=separation, + orderInverse=orderInverse, + overlay=overlay, + scalebar=scalebar, + legend=legend, + colorList=colorList, + returnPlotter=returnPlotter, + **kwargs) + + +@exception +def plotTimeSeries( + data, + label='TimeSeries', + title='Time Series Plot', + yLabel=None, + axis=None, + pop=None, + separation=1.0, + orderInverse=True, + overlay=False, + scalebar=True, + legend=True, + colorList=None, + returnPlotter=False, + **kwargs +): + """Function to produce a line plot of electrode signals (currently, LFP or CSD) + + Parameters + ---------- + data : dict, str + The data necessary to plot the signals. + + axis : matplotlib axis + The axis to plot into, allowing overlaying of plots. + + *Default:* ``None`` produces a new figure and axis. + + pop : str + A population name to calculate signals from. + + *Default:* ``None`` uses all populations. + + separation : float + Use to increase or decrease distance between signals on the plot. + + *Default:* ``1.0`` + + orderInverse : bool + Whether to invert the order of plotting. + + *Default:* ``True`` + + overlay : bool + Option to label signals with a color-matched overlay. + + *Default:* ``False`` + + scalebar : bool + Whether to to add a scalebar to the plot. + + *Default:* ``True`` + + legend : bool + Whether or not to add a legend to the plot. + + *Default:* ``True`` adds a legend. + + colorList : list + A *list* of colors to draw from when plotting. + + *Default:* ``None`` uses the default NetPyNE colorList. + + returnPlotter : bool + Whether to return the figure or the NetPyNE MetaFig object. + + *Default:* ``False`` returns the figure. + + + Plot Options + ------------ + showFig : bool + Whether to show the figure. + + *Default:* ``False`` + + saveFig : bool + Whether to save the figure. + + *Default:* ``False`` + + overwrite : bool + whether to overwrite existing figure files. + + *Default:* ``True`` overwrites the figure file + + *Options:* ``False`` adds a number to the file name to prevent overwriting + + legendKwargs : dict + a *dict* containing any or all legend kwargs. These include ``'title'``, ``'loc'``, ``'fontsize'``, ``'bbox_to_anchor'``, ``'borderaxespad'``, and ``'handlelength'``. + + rcParams : dict + a *dict* containing any or all matplotlib rcParams. To see all options, execute ``import matplotlib; print(matplotlib.rcParams)`` in Python. Any options in this *dict* will be used for this current figure and then returned to their prior settings. + + title : str + the axis title + + xlabel : str + label for x-axis + + ylabel : str + label for y-axis + + linewidth : int + line width + + alpha : float + line opacity (0-1) + + + Returns + ------- + time series plot : *matplotlib figure* + By default, returns the *figure*. If ``returnPlotter`` is ``True``, instead returns the NetPyNE MetaFig. + + """ + # If input is a dictionary, pull the data out of it - if type(LFPData) == dict: + if type(data) == dict: - t = LFPData['t'] - lfps = LFPData['electrodes']['lfps'] - names = LFPData['electrodes']['names'] - locs = LFPData['electrodes']['locs'] - colors = LFPData.get('colors') - linewidths = LFPData.get('linewidths') - alphas = LFPData.get('alphas') - axisArgs = LFPData.get('axisArgs') + t = data['t'] + lfps = data['electrodes']['data'] + names = data['electrodes']['names'] + locs = data['electrodes']['locs'] + colors = data.get('colors') + linewidths = data.get('linewidths') + alphas = data.get('alphas') + axisArgs = data.get('axisArgs') # Set up colors, linewidths, and alphas for the plots if not colors: @@ -229,12 +517,12 @@ def plotLFPTimeSeries( # Create a dictionary to hold axis inputs if not axisArgs: axisArgs = {} - title = 'LFP Time Series Plot' if pop: title += ' - Population: ' + pop axisArgs['title'] = title axisArgs['xlabel'] = 'Time (ms)' - axisArgs['ylabel'] = 'LFP Signal (uV)' + if yLabel: + axisArgs['ylabel'] = yLabel # Link colors to traces, make avg plot black, add separation to traces plotColors = [] @@ -274,7 +562,7 @@ def plotLFPTimeSeries( kwargs.pop(kwarg) # create Plotter object - linesPlotter = LinesPlotter(data=linesData, kind='LFPTimeSeries', axis=axis, **axisArgs, **kwargs) + linesPlotter = LinesPlotter(data=linesData, kind=label, axis=axis, **axisArgs, **kwargs) metaFig = linesPlotter.metafig # Set up the default legend settings @@ -338,10 +626,10 @@ def plotLFPTimeSeries( ) # Generate the figure - LFPTimeSeriesPlot = linesPlotter.plot(**axisArgs, **kwargs) + timeSeriesPlot = linesPlotter.plot(**axisArgs, **kwargs) # Default is to return the figure, but you can also return the plotter if returnPlotter: return metaFig else: - return LFPTimeSeriesPlot + return timeSeriesPlot diff --git a/netpyne/plotting/plotLFPPSD.py b/netpyne/plotting/plotTimeSeriesPSD.py similarity index 56% rename from netpyne/plotting/plotLFPPSD.py rename to netpyne/plotting/plotTimeSeriesPSD.py index b4d6f305e..dca158548 100644 --- a/netpyne/plotting/plotLFPPSD.py +++ b/netpyne/plotting/plotTimeSeriesPSD.py @@ -252,6 +252,299 @@ def plotLFPPSD( ) print('Plotting LFP power spectral density (PSD)...') + return plotTimeSeriesPSD( + PSDData=PSDData, + label='LFPPSD', + title='LFP Power Spectral Density', + yLabel=r'Power (mV$^2$ / Hz )', + axis=axis, + pop=pop, + separation=separation, + roundOffset=roundOffset, + orderInverse=orderInverse, + legend=legend, + colorList=colorList, + returnPlotter=returnPlotter, + **kwargs + ) + + + +@exception +def plotCSDPSD( + PSDData=None, + LFPData=None, + axis=None, + timeRange=None, + electrodes=['avg', 'all'], + pop=None, + separation=1.0, + roundOffset=True, + NFFT=256, + noverlap=128, + nperseg=256, + minFreq=1, + maxFreq=100, + stepFreq=1, + smooth=0, + logy=False, + normSignal=False, + normPSD=False, + filtFreq=False, + filtOrder=3, + detrend=False, + transformMethod='morlet', + orderInverse=True, + legend=True, + colorList=None, + returnPlotter=False, + **kwargs +): + """Function to produce a line plot of LFP electrode signals + + NetPyNE Options + --------------- + sim : NetPyNE sim object + The *sim object* from which to get data. + + *Default:* ``None`` uses the current NetPyNE sim object + + Parameters + ---------- + PSDData : dict, str + The data necessary to plot the LFP PSD. + + *Default:* ``None`` uses ``analysis.preparePSD`` to produce ``PSDData`` using the current NetPyNE sim object. + + If a *str* it must represent a file path to previously saved data. + + axis : matplotlib axis + The axis to plot into, allowing overlaying of plots. + + *Default:* ``None`` produces a new figure and axis. + + timeRange : list + Time range to include in the raster: ``[min, max]``. + + *Default:* ``None`` uses the entire simulation + + electrodes : list + A *list* of the electrodes to plot from. + + *Default:* ``['avg', 'all']`` plots each electrode as well as their average + + pop : str + A population name to calculate PSD from. + + *Default:* ``None`` uses all populations. + + separation : float + Use to increase or decrease distance between signals on the plot. + + *Default:* ``1.0`` + + roundOffset : bool + Attempts to line up PSD signals with gridlines + + *Default:* ``True`` + + NFFT : int (power of 2) + Number of data points used in each block for the PSD and time-freq FFT. + **Default:** ``256`` + + noverlap : int ( datband.shape[1]: # take CSD along smaller dimension - ax = 1 - else: - ax = 0 - # can change default to run Vaknin on bandpass filtered LFPs before calculating CSD, that - # way would have same number of channels in CSD and LFP (but not critical, and would take more RAM); - if vaknin: - datband = Vaknin(datband) - if norm: - removemean(datband, ax=ax) - # NB: when drawing CSD make sure that negative values (depolarizing intracellular current) drawn in red, - # and positive values (hyperpolarizing intracellular current) drawn in blue - CSD = ( - -numpy.diff(datband, n=2, axis=ax) / spacing**2 - ) # now each column (or row) is an electrode -- CSD along electrodes - return CSD From 5bbafa1cd8661b49c725ab3c600f2efe320010c8 Mon Sep 17 00:00:00 2001 From: vvbragin Date: Tue, 19 Sep 2023 19:29:09 +0200 Subject: [PATCH 10/19] VERSION 1.0.5 --- CHANGES.md | 10 +++++++++- doc/source/conf.py | 4 ++-- netpyne/__init__.py | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 028dd4de7..b9d5e5060 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,4 @@ -# Version in development +# Version 1.0.5 **New features** @@ -6,6 +6,8 @@ - Added colorbar to CSD plot +- Support for Sun Grid Engine HPC + - Extended sim.gatherData() with more optional arguments for flexibility - Specify linear/log scale in `plotRatePSD()` @@ -14,6 +16,10 @@ - Check RxD specification for potential syntax issues +- Prevent zero population size in scaled-down models + +- Better messages for validation errors + **Bug fixes** - Fixed errors in plotting with sim.cfg.gatherOnlySimData=True @@ -26,6 +32,8 @@ - Fixed performance issue in `plotConn()` with `groupBy='pop'` (default) +- Fixed `recordTraces` to not require more presicion than segment size (for stim and synMech) + # Version 1.0.4.2 **Bug fixes** diff --git a/doc/source/conf.py b/doc/source/conf.py index 646653a17..e5f737a02 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -67,9 +67,9 @@ # built documents. # # The short X.Y version. -version = '1.0.4.2' +version = '1.0.5' # The full version, including alpha/beta/rc tags. -release = '1.0.4.2' +release = '1.0.5' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/netpyne/__init__.py b/netpyne/__init__.py index 869b83cf6..2c9098ded 100644 --- a/netpyne/__init__.py +++ b/netpyne/__init__.py @@ -4,7 +4,7 @@ NetPyNE consists of a number of sub-packages and modules. """ -__version__ = '1.0.4.2' +__version__ = '1.0.5' import os, sys display = os.getenv('DISPLAY') From f3ddec6a32858957e84d787c5ce8892b705a84af Mon Sep 17 00:00:00 2001 From: vvbragin Date: Mon, 2 Oct 2023 20:54:52 +0200 Subject: [PATCH 11/19] Fixed loading point cell params from legacy models (iincl. ssue 607). Re-factored cell class detection in pop.py --- CHANGES.md | 8 +++ netpyne/cell/compartCell.py | 7 ++ netpyne/cell/pointCell.py | 2 + netpyne/network/pop.py | 137 +++++++++++++++++------------------- 4 files changed, 81 insertions(+), 73 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b9d5e5060..271b04b96 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,11 @@ +# Version in development + +**New features** + +**Bug fixes** + +- Fixed loading point cell params from legacy models (issue 607) + # Version 1.0.5 **New features** diff --git a/netpyne/cell/compartCell.py b/netpyne/cell/compartCell.py index a44a73c1a..39c3aa036 100644 --- a/netpyne/cell/compartCell.py +++ b/netpyne/cell/compartCell.py @@ -108,6 +108,13 @@ def create(self, createNEURONObj=None): conditionsMet = False if conditionsMet: # if all conditions are met, set values for this cell + + # Intercept possible issue where the cell is designed to be PointCell but misclassfied as CompartCell in Pop._setCellClass() + # (may happen if .mod not compiled or mech name misspelled) + assert 'secs' in prop, \ + f"""Cell rule labeled '{propLabel}' is a compartment cell, but it doesn't have required entry 'secs'. +If this cell is expected to be a point cell instead, make sure the correspondent mechanism is included and compiled.""" + if sim.cfg.includeParamsLabel: if 'label' not in self.tags: self.tags['label'] = [propLabel] # create list of property sets diff --git a/netpyne/cell/pointCell.py b/netpyne/cell/pointCell.py index 37216044f..86a921b5a 100644 --- a/netpyne/cell/pointCell.py +++ b/netpyne/cell/pointCell.py @@ -66,6 +66,8 @@ def __init__(self, gid, tags, create=True, associateGid=True): if 'params' in self.tags: dictParams = sim.replaceDictODict(self.tags.pop('params')) self.params = deepcopy(dictParams) + else: + self.params = {} if create and sim.cfg.createNEURONObj: self.createNEURONObj() # create cell diff --git a/netpyne/network/pop.py b/netpyne/network/pop.py index ce19b64a4..f34a2e75f 100644 --- a/netpyne/network/pop.py +++ b/netpyne/network/pop.py @@ -21,6 +21,7 @@ from numpy import pi, sqrt, sin, cos, arccos import numpy as np from neuron import h # Import NEURON +from netpyne import sim ############################################################################### @@ -39,7 +40,11 @@ def __init__(self, label, tags): self.tags = tags # list of tags/attributes of population (eg. numCells, cellModel,...) self.tags['pop'] = label self.cellGids = [] # list of cell gids beloging to this pop + self._setCellClass() # set type of cell + if self.cellModelClass == sim.PointCell: + self.__handlePointCellParams() + self.rand = h.Random() # random number generator def _distributeCells(self, numCellsPop): @@ -47,8 +52,6 @@ def _distributeCells(self, numCellsPop): Distribute cells across compute nodes using round-robin """ - from .. import sim - hostCells = {} for i in range(sim.nhosts): hostCells[i] = [] @@ -106,8 +109,6 @@ def createCellsFixedNum(self): Create population cells based on fixed number of cells """ - from .. import sim - cells = [] self.rand.Random123(self.tags['numCells'], sim.net.lastGid, sim.cfg.seeds['loc']) self.rand.uniform(0, 1) @@ -207,8 +208,6 @@ def createCellsDensity(self): Create population cells based on density """ - from .. import sim - cells = [] shape = sim.net.params.shape sizeX = sim.net.params.sizeX @@ -372,8 +371,6 @@ def createCellsList(self): Create population cells based on list of individual cells """ - from .. import sim - cells = [] self.tags['numCells'] = len(self.tags['cellsList']) for i in self._distributeCells(len(self.tags['cellsList']))[sim.rank]: @@ -411,8 +408,6 @@ def createCellsGrid(self): Create population cells based on fixed number of cells """ - from .. import sim - cells = [] rangeLocs = [[0, getattr(sim.net.params, 'size' + coord)] for coord in ['X', 'Y', 'Z']] @@ -462,7 +457,6 @@ def createCellsGrid(self): return cells def _createCellTags(self): - from .. import sim # copy all pop tags to cell tags, except those that are pop-specific cellTags = {k: v for (k, v) in self.tags.items() if k in sim.net.params.popTagsCopiedToCells} @@ -474,20 +468,6 @@ def _setCellClass(self): Set cell class (CompartCell, PointCell, etc) """ - from .. import sim - - # obtain cellModel either from cellParams or popParams - if ( - 'cellType' in self.tags - and self.tags['cellType'] in sim.net.params.cellParams - and 'cellModel' in sim.net.params.cellParams[self.tags['cellType']] - ): - cellModel = sim.net.params.cellParams[self.tags['cellType']]['cellModel'] - elif 'cellModel' in self.tags: - cellModel = self.tags['cellModel'] - else: - cellModel = None - # Check whether it's a NeuroML2 based cell # ! needs updating to read cellModel info from cellParams if 'originalFormat' in self.tags: @@ -495,64 +475,77 @@ def _setCellClass(self): self.cellModelClass = sim.NML2Cell if self.tags['originalFormat'] == 'NeuroML2_SpikeSource': self.cellModelClass = sim.NML2SpikeSource - else: + # obtain cellModel either from popParams.. + cellModel = self.tags.get('cellModel') + + if cellModel is None: + # .. or from cellParams + cellType = self.tags.get('cellType') + if cellType: + cellRule = sim.net.params.cellParams.get(cellType, {}) + cellModel = cellRule.get('cellModel') + else: + # TODO: or throw error? + pass + # set cell class: CompartCell for compartmental cells of PointCell for point neurons (NetStims, IntFire1,...) - try: # check if cellModel corresponds to an existing point process mechanism; if so, use PointCell - tmp = getattr(h, cellModel) + if cellModel and hasattr(h, cellModel): + # check if cellModel corresponds to an existing point process mechanism; if so, use PointCell self.cellModelClass = sim.PointCell - excludeTags = [ - 'pop', - 'cellModel', - 'cellType', - 'numCells', - 'density', - 'cellsList', - 'gridSpacing', - 'xRange', - 'yRange', - 'zRange', - 'xnormRange', - 'ynormRange', - 'znormRange', - 'vref', - 'spkTimes', - 'dynamicRates', - ] - params = {k: v for k, v in self.tags.items() if k not in excludeTags} - self.tags['params'] = params - for k in self.tags['params']: - self.tags.pop(k) - sim.net.params.popTagsCopiedToCells.append('params') - - # if point cell params defined directly in pop params, need to scan them for string functions - if len(params): - from ..specs.netParams import CellParams - - CellParams.updateStringFuncsWithPopParams(self.tags['pop'], params) - except: - if getattr(self.tags, 'cellModel', None) in [ - 'NetStim', - 'DynamicNetStim', - 'VecStim', - 'IntFire1', - 'IntFire2', - 'IntFire4', - ]: + else: + # otherwise assume has sections and some cellParam rules apply to it; use CompartCell + self.cellModelClass = sim.CompartCell + # if model is known but wasn't recognized, issue warning + knownPointps = ['NetStim', 'DynamicNetStim', 'VecStim', 'IntFire1', 'IntFire2', 'IntFire4'] + if getattr(self.tags, 'cellModel', None) in knownPointps: print( 'Warning: could not find %s point process mechanism required for population %s' % (cellModel, self.tags['pop']) ) - self.cellModelClass = ( - sim.CompartCell - ) # otherwise assume has sections and some cellParam rules apply to it; use CompartCell + + def __handlePointCellParams(self): + + if 'params' in self.tags and isinstance(self.tags['params'], dict): + # in some cases, params for point cell may already be grouped in the nested 'params' dict. + params = self.tags['params'] + else: + # otherwise, try extracting them from the top level of tags dict + excludeTags = [ + 'pop', + 'cellModel', + 'cellType', + 'numCells', + 'density', + 'cellsList', + 'gridSpacing', + 'xRange', + 'yRange', + 'zRange', + 'xnormRange', + 'ynormRange', + 'znormRange', + 'vref', + 'spkTimes', + 'dynamicRates', + ] + params = {k: v for k, v in self.tags.items() if k not in excludeTags} + self.tags['params'] = params + for k in self.tags['params']: + self.tags.pop(k) + sim.net.params.popTagsCopiedToCells.append('params') + + # if point cell params defined directly in pop params, need to scan them for string functions + if len(params): + from ..specs.netParams import CellParams + + CellParams.updateStringFuncsWithPopParams(self.tags['pop'], params) + def calcRelativeSegCoords(self): """Calculate segment coordinates from 3d point coordinates Used for LFP calc (one per population cell; assumes same morphology)""" - from .. import sim - localPopGids = list(set(sim.net.gid2lid.keys()).intersection(set(self.cellGids))) if localPopGids: cell = sim.net.cells[sim.net.gid2lid[localPopGids[0]]] @@ -621,8 +614,6 @@ def calcRelativeSegCoords(self): def __getstate__(self): """Removes non-picklable h objects so can be pickled and sent via py_alltoall""" - from .. import sim - odict = self.__dict__.copy() # copy the dict since we change it odict = sim.replaceFuncObj(odict) # replace h objects with None so can be pickled # odict['cellModelClass'] = str(odict['cellModelClass']) From 4a898adc06f41f05c13b64f911d0515685a84477 Mon Sep 17 00:00:00 2001 From: vvbragin Date: Tue, 10 Oct 2023 20:54:40 +0200 Subject: [PATCH 12/19] extracted conditions checking code to separate method --- netpyne/cell/cell.py | 31 ++----- netpyne/cell/compartCell.py | 158 ++++++------------------------------ netpyne/sim/utils.py | 37 +++++++++ 3 files changed, 72 insertions(+), 154 deletions(-) diff --git a/netpyne/cell/cell.py b/netpyne/cell/cell.py index 171438510..de12a5923 100644 --- a/netpyne/cell/cell.py +++ b/netpyne/cell/cell.py @@ -186,29 +186,8 @@ def recordTraces(self): conditionsMet = 1 if 'conds' in params: - for (condKey, condVal) in params['conds'].items(): # check if all conditions are met - # choose what to comapare to - if condKey in ['gid']: # CHANGE TO GID - compareTo = self.gid - else: - compareTo = self.tags[condKey] - - # check if conditions met - if isinstance(condVal, list) and isinstance(condVal[0], Number): - if compareTo == self.gid: - if compareTo not in condVal: - conditionsMet = 0 - break - elif compareTo < condVal[0] or compareTo > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal, list) and isinstance(condVal[0], basestring): - if compareTo not in condVal: - conditionsMet = 0 - break - elif compareTo != condVal: - conditionsMet = 0 - break + conditionsMet = self.checkConditions(params['conds']) + if conditionsMet: try: ptr = None @@ -373,6 +352,12 @@ def recordTraces(self): # else: # if sim.cfg.verbose: print ' NOT recording ', key, 'from cell ', self.gid, ' with parameters: ',str(params) + + def checkConditions(self, conditions): + from ..sim.utils import checkConditions + return checkConditions(conditions=conditions, against=self.tags, cellGid=self.gid) + + def _randomizer(self): try: return self.__cellParamsRand diff --git a/netpyne/cell/compartCell.py b/netpyne/cell/compartCell.py index 39c3aa036..d0f1c4646 100644 --- a/netpyne/cell/compartCell.py +++ b/netpyne/cell/compartCell.py @@ -90,19 +90,7 @@ def create(self, createNEURONObj=None): for propLabel, prop in sim.net.params.cellParams.items(): # for each set of cell properties conditionsMet = 1 if 'conds' in prop and len(prop['conds']) > 0: - for (condKey, condVal) in prop['conds'].items(): # check if all conditions are met - if isinstance(condVal, list): - if isinstance(condVal[0], Number): - if self.tags.get(condKey) < condVal[0] or self.tags.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal[0], basestring): - if self.tags.get(condKey) not in condVal: - conditionsMet = 0 - break - elif self.tags.get(condKey) != condVal: - conditionsMet = 0 - break + conditionsMet = self.checkConditions(prop['conds']) elif self.tags['cellType'] != propLabel: # simplified method for defining cell params (when no 'conds') conditionsMet = False @@ -132,32 +120,14 @@ def create(self, createNEURONObj=None): def modify(self, prop): from .. import sim - conditionsMet = 1 - for (condKey, condVal) in prop['conds'].items(): # check if all conditions are met - if condKey == 'label': - if condVal not in self.tags['label']: - conditionsMet = 0 - break - elif isinstance(condVal, list): - if isinstance(condVal[0], Number): - if self.tags.get(condKey) < condVal[0] or self.tags.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal[0], basestring): - if self.tags.get(condKey) not in condVal: - conditionsMet = 0 - break - elif self.tags.get(condKey) != condVal: - conditionsMet = 0 - break + conditionsMet = self.checkConditions(prop['conds']) if conditionsMet: # if all conditions are met, set values for this cell if sim.cfg.createPyStruct: self.createPyStruct(prop) if sim.cfg.createNEURONObj: - self.createNEURONObj( - prop - ) # add sections, mechanisms, synaptic mechanisms, geometry and topolgy specified by this property set + # add sections, mechanisms, synaptic mechanisms, geometry and topolgy specified by this property set + self.createNEURONObj(prop) def createPyStruct(self, prop): from .. import sim @@ -847,45 +817,28 @@ def modifySynMechs(self, params): conditionsMet = 1 if 'cellConds' in params: - if conditionsMet: - for (condKey, condVal) in params['cellConds'].items(): # check if all conditions are met - # check if conditions met - if isinstance(condVal, list): - if self.tags.get(condKey) < condVal[0] or self.tags.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif self.tags.get(condKey) != condVal: - conditionsMet = 0 - break + conditionsMet = self.checkConditions(params['cellConds']) if conditionsMet: for secLabel, sec in self.secs.items(): for synMech in sec['synMechs']: conditionsMet = 1 if 'conds' in params: - for (condKey, condVal) in params['conds'].items(): # check if all conditions are met - # check if conditions met - if condKey == 'sec': - if condVal != secLabel: - conditionsMet = 0 - break - elif isinstance(condVal, list) and isinstance(condVal[0], Number): - if synMech.get(condKey) < condVal[0] or synMech.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal, list) and isinstance(condVal[0], basestring): - if synMech.get(condKey) not in condVal: - conditionsMet = 0 - break - elif synMech.get(condKey) != condVal: - conditionsMet = 0 - break + # first check if section matches + secLabelInConds = params['conds'].get('sec') + if secLabelInConds and (secLabelInConds != secLabel): + # skip to next section + break + + # then check the rest of conds + synMechConds = deepcopy(params['conds']) + synMechConds.pop('sec') + conditionsMet = sim.utils.checkConditions(synMechConds, against=synMech) if conditionsMet: # if all conditions are met, set values for this cell exclude = ['conds', 'cellConds'] + SynMechParams.reservedKeys() - for synParamName, synParamValue in { - k: v for k, v in params.items() if k not in exclude - }.items(): + paramsToModify = {k: v for k, v in params.items() if k not in exclude} + for synParamName, synParamValue in paramsToModify.items(): if sim.cfg.createPyStruct: synMech[synParamName] = synParamValue if sim.cfg.createNEURONObj: @@ -1198,59 +1151,22 @@ def modifyConns(self, params): conditionsMet = 1 if 'conds' in params: - for (condKey, condVal) in params['conds'].items(): # check if all conditions are met - # choose what to comapare to - if condKey in ['postGid']: - compareTo = self.gid - else: - compareTo = conn.get(condKey) + conds = params['conds'] - # check if conditions met - if isinstance(condVal, list) and isinstance(condVal[0], Number): - if compareTo < condVal[0] or compareTo > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal, list) and isinstance(condVal[0], basestring): - if compareTo not in condVal: - conditionsMet = 0 - break - elif compareTo != condVal: - conditionsMet = 0 - break + # `conds` may contain `postGid` key, which is deprecated in favour of having `gid` key in `postConds`, + # but for backward compatibility, try to detect it here and replace with `gid`, so checkConditions() can process it: + if 'postGid' in conds: + conds['gid'] = conds.pop('postGid') + conditionsMet = sim.utils.checkConditions(conds, against=conn, cellGid=self.gid) if conditionsMet and 'postConds' in params: - for (condKey, condVal) in params['postConds'].items(): # check if all conditions are met - # check if conditions met - if isinstance(condVal, list) and isinstance(condVal[0], Number): - if self.tags.get(condKey) < condVal[0] or self.tags.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal, list) and isinstance(condVal[0], basestring): - if self.tags.get(condKey) not in condVal: - conditionsMet = 0 - break - elif self.tags.get(condKey) != condVal: - conditionsMet = 0 - break + conditionsMet = self.checkConditions(params['postConds']) if conditionsMet and 'preConds' in params: try: cell = sim.net.cells[conn['preGid']] - if cell: - for (condKey, condVal) in params['preConds'].items(): # check if all conditions are met - # check if conditions met - if isinstance(condVal, list) and isinstance(condVal[0], Number): - if cell.tags.get(condKey) < condVal[0] or cell.tags.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal, list) and isinstance(condVal[0], basestring): - if cell.tags.get(condKey) not in condVal: - conditionsMet = 0 - break - elif cell.tags.get(condKey) != condVal: - conditionsMet = 0 - break + conditionsMet = cell.checkConditions(params['preConds']) except: pass # print('Warning: modifyConns() does not yet support conditions of presynaptic cells when running parallel sims') @@ -1279,34 +1195,14 @@ def modifyStims(self, params): conditionsMet = 1 if 'cellConds' in params: if conditionsMet: - for (condKey, condVal) in params['cellConds'].items(): # check if all conditions are met - # check if conditions met - if isinstance(condVal, list): - if self.tags.get(condKey) < condVal[0] or self.tags.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif self.tags.get(condKey) != condVal: - conditionsMet = 0 - break + conditionsMet = self.checkConditions(params['cellConds']) if conditionsMet == 1: for stim in self.stims: conditionsMet = 1 if 'conds' in params: - for (condKey, condVal) in params['conds'].items(): # check if all conditions are met - # check if conditions met - if isinstance(condVal, list) and isinstance(condVal[0], Number): - if stim.get(condKey) < condVal[0] or stim.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal, list) and isinstance(condVal[0], basestring): - if stim.get(condKey) not in condVal: - conditionsMet = 0 - break - elif stim.get(condKey) != condVal: - conditionsMet = 0 - break + conditionsMet = sim.utils.checkConditions(params['conds'], against=stim) if conditionsMet: # if all conditions are met, set values for this cell if stim['type'] == 'NetStim': # for netstims, find associated netcon diff --git a/netpyne/sim/utils.py b/netpyne/sim/utils.py index 6bc0a37a9..e408bfec8 100644 --- a/netpyne/sim/utils.py +++ b/netpyne/sim/utils.py @@ -1017,6 +1017,43 @@ def clearAll(): gc.collect() +def checkConditions(conditions, against, cellGid=None): + + conditionsMet = 1 + for (condKey, condVal) in conditions.items(): + + # gid matching will be processed in specific way + gidCompare = False + if (cellGid is not None) and (condKey == 'gid'): + compareTo = cellGid + gidCompare = True + + else: + compareTo = against.get(condKey) + + if isinstance(condVal, list): + if isinstance(condVal[0], Number): + if gidCompare: + if compareTo not in condVal: + conditionsMet = 0 + break + elif compareTo < condVal[0] or compareTo > condVal[1]: + conditionsMet = 0 + break + elif isinstance(condVal[0], basestring): + if compareTo not in condVal: + conditionsMet = 0 + break + elif isinstance(compareTo, list): # e.g. to match 'label', which may be list + if condVal not in compareTo: + conditionsMet = 0 + break + elif compareTo != condVal: + conditionsMet = 0 + break + return conditionsMet + + # ------------------------------------------------------------------------------ # Create a subclass of json.JSONEncoder to convert numpy types in Python types # ------------------------------------------------------------------------------ From 2b5825527a321aea7a065af32f0c79aa04e7c44d Mon Sep 17 00:00:00 2001 From: jchen6727 Date: Thu, 19 Oct 2023 17:59:27 -0400 Subject: [PATCH 13/19] docstring for Batch (#780) update grid and util for sge --- netpyne/batch/batch.py | 43 +++++++++++++++++++++++++++++++++++++----- netpyne/batch/grid.py | 4 ++-- netpyne/batch/utils.py | 2 ++ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/netpyne/batch/batch.py b/netpyne/batch/batch.py index 31fd07b99..0cb831917 100644 --- a/netpyne/batch/batch.py +++ b/netpyne/batch/batch.py @@ -75,9 +75,38 @@ def tupleToStr(obj): # ------------------------------------------------------------------------------- class Batch(object): """ - Class for/to - - + Class that handles batch simulations on NetPyNE. + Relevant Attributes: + batchLabel : str + The label of the batch used for directory/file naming of batch generated files. + cfgFile : str + The path of the file containing the `netpyne.simConfig.SimConfig` object + cfg : `netpyne.simConfig.SimConfig` + The `netpyne.simConfig.SimConfig` object + N.B. either cfg or cfgFile should be specified #TODO: replace with typechecked single argument + netParamsFile : str + The path of the file containing the `netpyne.netParams.NetParams` object + netParams : `netpyne.netParams.NetParams` + The `netpyne.netParams.NetParams` object + N.B. either netParams or netParamsFile should be specified #TODO: replace with typechecked single argument + initCfg : dict + params dictionary that is used to modify the batch cfg prior to any algorithm based parameter modifications + saveFolder : str + The path of the folder where the batch will be saved (defaults to batchLabel) + method : str + The algorithm method used for batch + runCfg : dict + Keyword: Arg dictionary used to generate submission templates (see utils.py) + evolCfg : dict #TODO: replace with algoCfg? to merge with optimCfg + Keyword: Arg dictionary used to define evolutionary algorithm parameters (see evol.py) + optimCfg : dict #TODO: replace with algoCfg? to merge with evolCfg + Keyword: Arg dictionary used to define optimization algorithm parameters + (see asd_parallel.py, optuna_parallel.py, sbi_parallel.py) + params : list + Dictionary of parameters to be explored per algorithm (grid, evol, asd, optuna, sbi) + (see relevant algorithm script for details) + seed : int + Seed for random number generator for some algorithms """ def __init__( @@ -88,19 +117,23 @@ def __init__( netParams=None, params=None, groupedParams=None, - initCfg={}, + initCfg=None, seed=None, ): self.batchLabel = 'batch_' + str(datetime.date.today()) self.cfgFile = cfgFile self.cfg = cfg self.netParams = netParams - self.initCfg = initCfg + if initCfg: + self.initCfg = initCfg + else: + self.initCfg = {} self.netParamsFile = netParamsFile self.saveFolder = '/' + self.batchLabel self.method = 'grid' self.runCfg = {} self.evolCfg = {} + self.optimCfg = {} self.params = [] self.seed = seed if params: diff --git a/netpyne/batch/grid.py b/netpyne/batch/grid.py index 6ea987cf1..adec7a77f 100644 --- a/netpyne/batch/grid.py +++ b/netpyne/batch/grid.py @@ -340,7 +340,7 @@ def gridSubmit(batch, pc, netParamsSavePath, jobName, simLabel, processes, proce 'walltime': walltime, 'vmem': '32G', 'queueName': 'cpu.q', - 'cores': 1, + 'cores': 2, 'pre': '', 'post': '', 'mpiCommand': 'mpiexec', #'log': "~/qsub/{}".format(jobName) @@ -350,7 +350,7 @@ def gridSubmit(batch, pc, netParamsSavePath, jobName, simLabel, processes, proce sge_args.update(batch.runCfg) #(batch, pc, netParamsSavePath, jobName, simLabel, processes, processFiles): - sge_args['command'] = '%s -n $NSLOTS nrniv -python -mpi %s simConfig=%s netParams=%s' % ( + sge_args['command'] = '%s -n $NSLOTS -hosts $(hostname) nrniv -python -mpi %s simConfig=%s netParams=%s' % ( sge_args['mpiCommand'], script, cfgSavePath, diff --git a/netpyne/batch/utils.py b/netpyne/batch/utils.py index 30b4aa361..9e65281ba 100644 --- a/netpyne/batch/utils.py +++ b/netpyne/batch/utils.py @@ -459,6 +459,8 @@ def jobPathAndNameForCand(index): elif type == 'hpc_sge': executer = 'qsub' queueName = args.get('queueName', 'default') + command = '%s -n $NSLOTS -hosts $(hostname) %s -python -mpi %s simConfig=%s netParams=%s' % ( + mpiCommand, nrnCommand, script, cfgSavePath, netParamsSavePath) jobString = jobStringHPCSGE( jobName, walltime, queueName, nodes, coresPerNode, jobPath, custom, command ) From 0a7317000d784a3bcc60ecf58a2ec7b5c3638ccf Mon Sep 17 00:00:00 2001 From: jchen6727 Date: Sat, 21 Oct 2023 13:54:44 -0400 Subject: [PATCH 14/19] Update cell input functions in netpyne/cell/inputs.py and user reference (#781) * cell inputs.py `tstop` versus `stop` * updated .rst for documentation, inputs * updated 'tstop', 'stop', integer for n_inputs --- doc/source/user_documentation.rst | 2 +- netpyne/cell/inputs.py | 28 +++++++++++++++++++--------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/doc/source/user_documentation.rst b/doc/source/user_documentation.rst index 6b9daefac..8843f4c64 100644 --- a/doc/source/user_documentation.rst +++ b/doc/source/user_documentation.rst @@ -305,7 +305,7 @@ Currently, 'rhythmic', 'evoked', 'poisson' and 'gauss' spike pattern generators * **evoked** - creates the ongoing external inputs (rhythmic) - * **start** - time of first spike. if -1, uniform distribution between startMin and startMax (ms) + * **start** - time of first spike * **inc** - increase in time of first spike; from cfg.inc_evinput (ms) diff --git a/netpyne/cell/inputs.py b/netpyne/cell/inputs.py index 07cf91716..8c525666b 100644 --- a/netpyne/cell/inputs.py +++ b/netpyne/cell/inputs.py @@ -32,14 +32,23 @@ def createRhythmicPattern(params, rand): - startStd: standard deviation of normal distrinution for start time (ms); mean is set by start param. Only used if > 0.0 - freq: oscillatory frequency of rhythmic pattern (Hz) - freqStd: standard deviation of oscillatory frequency (Hz) - - distribution: distribution type fo oscillatory frequencies; either 'normal' or 'uniform' + - distribution: distribution type for oscillatory frequencies; either 'normal' or 'uniform' - eventsPerCycle: spikes/burst per cycle; should be either 1 or 2 - repeats: number of times to repeat input pattern (equivalent to number of inputs) - stop: maximum time for last spike of pattern (ms) + - tstop: maximum time for last spike of pattern (ms) (maintained for backward compatibility) """ - + #TODO catch KeyError cases? # start is always defined start = params['start'] + if 'stop' in params: + stop = params['stop'] + elif 'tstop' in params: + stop = params['tstop'] + else: + print('stop / tstop time not defined, please provide stop time') + return np.array([]) + # If start is -1, randomize start time of inputs if start == -1: startMin = params.get('startMin', 25.0) @@ -47,7 +56,7 @@ def createRhythmicPattern(params, rand): start = rand.uniform(startMin, startMax) elif params.get('startStd', -1) > 0.0: # randomize start time based on startStd start = rand.normal(start, params['startStd']) # start time uses different prng - freq = params.get('freq', 0) + freq = params.get('freq', False) # if freq not supplied, "exit" control flow w/ print statement freqStd = params.get('freqStd', 0) eventsPerCycle = params.get('eventsPerCycle', 2) distribution = params.get('distribution', 'normal') @@ -55,12 +64,13 @@ def createRhythmicPattern(params, rand): if eventsPerCycle > 2 or eventsPerCycle <= 0: print("eventsPerCycle should be either 1 or 2, trying 2") eventsPerCycle = 2 - # If frequency is 0, create empty vector if input times - if not freq: + # If frequency is False, create empty vector if input times + if not freq: #TODO clarification, why not raise?, should error? + print("No frequency specified. Not making any alpha feeds.") t_input = [] elif distribution == 'normal': # array of mean stimulus times, starts at start - isi_array = np.arange(start, params['stop'], 1000.0 / freq) + isi_array = np.arange(start, stop, 1000.0 / freq) # array of single stimulus times -- no doublets if freqStd: # t_array = self.prng.normal(np.repeat(isi_array, self.p_ext['repeats']), stdev) @@ -86,8 +96,8 @@ def createRhythmicPattern(params, rand): t_input.sort() # Uniform Distribution elif distribution == 'uniform': - n_inputs = params['repeats'] * freq * (params['tstop'] - start) / 1000.0 - t_array = rand.uniform(start, params['tstop'], n_inputs) + n_inputs = params['repeats'] * freq * (stop - start) / 1000.0 + t_array = rand.uniform(start, stop, int(n_inputs)) if eventsPerCycle == 2: # Two arrays store doublet times t_input_low = t_array - 5 @@ -111,7 +121,7 @@ def createEvokedPattern(params, rand, inc=0): """ creates the ongoing external inputs (rhythmic) input params: - - start: time of first spike. if -1, uniform distribution between startMin and startMax (ms) + - start: time of first spike. - inc: increase in time of first spike; from cfg.inc_evinput (ms) - startStd: standard deviation of start (ms) - numspikes: total number of spikes to generate From dae3b77a559c22a89bd85c062d30e8e479a2f72a Mon Sep 17 00:00:00 2001 From: Eugenio Urdapilleta Date: Mon, 23 Oct 2023 16:56:51 -0300 Subject: [PATCH 15/19] Raster plot colored by phase + bug fixes **New features** - Raster plot colored by phase - Examples based on the CA3 model using the 'colorbyPhase' option in the plotRaster **Bug fixes** - Fix voltage movie tutorial - Fix to automatically include netstims in the sim.allSimData object when plotRaster 'include' selects 'all' --- CHANGES.md | 8 + .../index.npjson | 6 + .../mod/CA1ih.mod | 64 ++++ .../mod/CA1ika.mod | 85 +++++ .../mod/CA1ikdr.mod | 60 ++++ .../mod/CA1ina.mod | 89 +++++ .../mod/MyExp2SynBB.mod | 67 ++++ .../mod/MyExp2SynNMDABB.mod | 108 ++++++ .../mod/aux_fun.inc | 43 +++ .../mod/caolmw.mod | 47 +++ .../mod/icaolmw.mod | 51 +++ .../mod/iholmw.mod | 60 ++++ .../mod/kcaolmw.mod | 52 +++ .../mod/kdrbwb.mod | 76 +++++ .../mod/nafbwb.mod | 81 +++++ .../CA3model_RasterColoredbyPhase/src/cfg.py | 34 ++ .../CA3model_RasterColoredbyPhase/src/init.py | 10 + .../src/netParams.py | 322 ++++++++++++++++++ netpyne/analysis/spikes.py | 109 +++++- netpyne/plotting/plotRaster.py | 50 ++- netpyne/plotting/plotter.py | 204 +++++++++-- netpyne/sim/setup.py | 2 +- .../voltage_movie_tut/voltage_movie_tut.py | 4 +- 23 files changed, 1590 insertions(+), 42 deletions(-) create mode 100644 examples/CA3model_RasterColoredbyPhase/index.npjson create mode 100644 examples/CA3model_RasterColoredbyPhase/mod/CA1ih.mod create mode 100644 examples/CA3model_RasterColoredbyPhase/mod/CA1ika.mod create mode 100644 examples/CA3model_RasterColoredbyPhase/mod/CA1ikdr.mod create mode 100644 examples/CA3model_RasterColoredbyPhase/mod/CA1ina.mod create mode 100644 examples/CA3model_RasterColoredbyPhase/mod/MyExp2SynBB.mod create mode 100644 examples/CA3model_RasterColoredbyPhase/mod/MyExp2SynNMDABB.mod create mode 100644 examples/CA3model_RasterColoredbyPhase/mod/aux_fun.inc create mode 100644 examples/CA3model_RasterColoredbyPhase/mod/caolmw.mod create mode 100644 examples/CA3model_RasterColoredbyPhase/mod/icaolmw.mod create mode 100644 examples/CA3model_RasterColoredbyPhase/mod/iholmw.mod create mode 100644 examples/CA3model_RasterColoredbyPhase/mod/kcaolmw.mod create mode 100644 examples/CA3model_RasterColoredbyPhase/mod/kdrbwb.mod create mode 100644 examples/CA3model_RasterColoredbyPhase/mod/nafbwb.mod create mode 100644 examples/CA3model_RasterColoredbyPhase/src/cfg.py create mode 100644 examples/CA3model_RasterColoredbyPhase/src/init.py create mode 100644 examples/CA3model_RasterColoredbyPhase/src/netParams.py diff --git a/CHANGES.md b/CHANGES.md index 271b04b96..bdcc2a5ce 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,10 +2,18 @@ **New features** +- Raster plot colored by phase + +- Examples based on the CA3 model using the 'colorbyPhase' option in the plotRaster + **Bug fixes** - Fixed loading point cell params from legacy models (issue 607) +- Fix voltage movie tutorial + +- Fix to automatically include netstims in the sim.allSimData object when plotRaster 'include' selects 'all' + # Version 1.0.5 **New features** diff --git a/examples/CA3model_RasterColoredbyPhase/index.npjson b/examples/CA3model_RasterColoredbyPhase/index.npjson new file mode 100644 index 000000000..20c14b4a6 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/index.npjson @@ -0,0 +1,6 @@ +{ + "mod_folder": "mod", + "simConfig": "src/cfg.py", + "python_run": "src/init.py", + "netParams": "src/netParams.py" +} \ No newline at end of file diff --git a/examples/CA3model_RasterColoredbyPhase/mod/CA1ih.mod b/examples/CA3model_RasterColoredbyPhase/mod/CA1ih.mod new file mode 100644 index 000000000..93d435e30 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/CA1ih.mod @@ -0,0 +1,64 @@ +: $Id: CA1ih.mod,v 1.4 2010/12/13 21:35:47 samn Exp $ +TITLE Ih CA3 + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) +} + +NEURON { + SUFFIX hcurrent + NONSPECIFIC_CURRENT ih + RANGE g, e, v50, htau, hinf + RANGE gfactor +} + +PARAMETER { + celsius (degC) + g= 0.0001 (mho/cm2) + e= -30 (mV) + v50=-82 (mV) + gfactor = 1 +} + +STATE { + h +} + +ASSIGNED { + ih (mA/cm2) + hinf + htau (ms) + v (mV) +} + +PROCEDURE iassign () { ih=g*h*(v-e)*gfactor } + +BREAKPOINT { + SOLVE states METHOD cnexp + iassign() +} + +DERIVATIVE states { + rates(v) + h'= (hinf- h)/ htau +} + +INITIAL { + rates(v) + h = hinf + iassign() +} + +PROCEDURE rates(v (mV)) { + UNITSOFF + : HCN1 + :hinf = 1/(1+exp(0.151*(v-v50))) + :htau = exp((0.033*(v+75)))/(0.011*(1+exp(0.083*(v+75)))) + + : HCN2 + hinf = 1/(1+exp((v-v50)/10.5)) + htau = (1/(exp(-14.59-0.086*v)+exp(-1.87+0.0701*v))) + UNITSON +} + diff --git a/examples/CA3model_RasterColoredbyPhase/mod/CA1ika.mod b/examples/CA3model_RasterColoredbyPhase/mod/CA1ika.mod new file mode 100644 index 000000000..9e4fe6922 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/CA1ika.mod @@ -0,0 +1,85 @@ +: $Id: CA1ika.mod,v 1.2 2010/12/01 05:06:07 samn Exp $ +TITLE Ika CA1 + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) +} + +NEURON { + SUFFIX kacurrent + NONSPECIFIC_CURRENT ika, ikad + RANGE g, gd, e, ninf, ntau, ndinf, ndtau, linf, ltau +} + +PARAMETER { + celsius (degC) + g= 0.048 (mho/cm2) + gd= 0 (mho/cm2) + e= -90 (mV) +} + +STATE { + n + nd : distal + l +} + +ASSIGNED { + v (mV) + ika (mA/cm2) + ikad (mA/cm2) + ninf + ntau (ms) + ndinf + ndtau (ms) + linf + ltau (ms) +} + +PROCEDURE iassign () { + ika=g*n*l*(v-e) + ikad=gd*nd*l*(v-e) +} + +BREAKPOINT { + SOLVE states METHOD cnexp + iassign() +} + +DERIVATIVE states { + rates(v) + n'= (ninf- n)/ ntau + l'= (linf- l)/ ltau + nd'= (ndinf-nd)/ndtau +} + +INITIAL { + rates(v) + n = ninf + l = linf + iassign() +} + +PROCEDURE rates(v (mV)) { + LOCAL a, b + UNITSOFF + a = exp(-0.038*(1.5+1/(1+exp(v+40)/5))*(v-11)) + b = exp(-0.038*(0.825+1/(1+exp(v+40)/5))*(v-11)) + ntau=4*b/(1+a) + if (ntau<0.1) {ntau=0.1} + ninf=1/(1+a) + + a=exp(-0.038*(1.8+1/(1+exp(v+40)/5))*(v+1)) + b=exp(-0.038*(0.7+1/(1+exp(v+40)/5))*(v+1)) + ndtau=2*b/(1+a) + if (ndtau<0.1) {ndtau=0.1} + ndinf=1/(1+a) + + a = exp(0.11*(v+56)) + ltau=0.26*(v+50) + if (ltau<2) {ltau=2} + linf=1/(1+a) + UNITSON +} + diff --git a/examples/CA3model_RasterColoredbyPhase/mod/CA1ikdr.mod b/examples/CA3model_RasterColoredbyPhase/mod/CA1ikdr.mod new file mode 100644 index 000000000..4c5236362 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/CA1ikdr.mod @@ -0,0 +1,60 @@ +: $Id: CA1ikdr.mod,v 1.2 2010/12/01 05:10:52 samn Exp $ +TITLE IKDR CA1 + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) +} + +NEURON { + SUFFIX kdrcurrent + NONSPECIFIC_CURRENT ik + RANGE g, e, ninf, ntau +} + +PARAMETER { + celsius (degC) + g = 0.010 (mho/cm2) + e = -90 (mV) +} + +STATE { + n +} + +ASSIGNED { + v (mV) + ik (mA/cm2) + ninf + ntau (ms) +} + +PROCEDURE iassign () { ik=g*n*(v-e) } + +BREAKPOINT { + SOLVE states METHOD cnexp + iassign() +} + +DERIVATIVE states { + rates(v) + n'= (ninf- n)/ ntau +} + +INITIAL { + rates(v) + n = ninf + iassign() +} + +PROCEDURE rates(v (mV)) { + LOCAL a, b + UNITSOFF + a = exp(-0.11*(v-13)) + b = exp(-0.08*(v-13)) + ntau=50*b/(1+a) + if (ntau<2) {ntau=2} + ninf=1/(1+a) + UNITSON +} + diff --git a/examples/CA3model_RasterColoredbyPhase/mod/CA1ina.mod b/examples/CA3model_RasterColoredbyPhase/mod/CA1ina.mod new file mode 100644 index 000000000..d33ab9739 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/CA1ina.mod @@ -0,0 +1,89 @@ +: $Id: CA1ina.mod,v 1.4 2010/11/30 19:50:00 samn Exp $ +TITLE INa CA1 + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) +} + +NEURON { + SUFFIX nacurrent + NONSPECIFIC_CURRENT ina + RANGE g, e, vi, ki + RANGE minf,hinf,iinf,mtau,htau,itau : testing +} + +PARAMETER { + : v (mV) + celsius (degC) + g = 0.032 (mho/cm2) + e = 55 (mV) + vi = -60 (mV) + ki = 0.8 +} + +STATE { + m + h + I : i +} + +ASSIGNED { + i (mA/cm2) + ina (mA/cm2) + minf + mtau (ms) + hinf + htau (ms) + iinf + itau (ms) + v (mV) : testing +} + +: PROCEDURE iassign () { ina=g*m*m*m*h*i*(v-e) } +PROCEDURE iassign () { i=g*m*m*m*h*I*(v-e) ina=i} + +BREAKPOINT { + SOLVE states METHOD cnexp + iassign() +} + +DERIVATIVE states { + rates(v) + m' = (minf - m) / mtau + h' = (hinf - h) / htau + : i' = (iinf - i) / itau + I' = (iinf - I) / itau +} + +INITIAL { + rates(v) + h = hinf + m = minf + : i = iinf + I = iinf + iassign() : testing +} + + +PROCEDURE rates(v (mV)) { + LOCAL a, b + UNITSOFF + a = 0.4*(v+30)/(1-exp(-(v+30)/7.2)) + b = 0.124*(v+30)/(exp((v+30)/7.2)-1) + mtau=0.5/(a+b) + if (mtau<0.02) {mtau=0.02} + minf=a/(a+b) + a = 0.03*(v+45)/(1-exp(-(v+45)/1.5)) + b = 0.01*(v+45)/(exp((v+45)/1.5)-1) + htau=0.5/(a+b) + if (htau<0.5) {htau=0.5} + hinf=1/(1+exp((v+50)/4)) + a = exp(0.45*(v+66)) + b = exp(0.09*(v+66)) + itau=3000*b/(1+a) + if (itau<10) {itau=10} + iinf=(1+ki*exp((v-vi)/2))/(1+exp((v-vi)/2)) + UNITSON +} + diff --git a/examples/CA3model_RasterColoredbyPhase/mod/MyExp2SynBB.mod b/examples/CA3model_RasterColoredbyPhase/mod/MyExp2SynBB.mod new file mode 100644 index 000000000..9a68baef1 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/MyExp2SynBB.mod @@ -0,0 +1,67 @@ +: $Id: MyExp2SynBB.mod,v 1.4 2010/12/13 21:27:51 samn Exp $ +NEURON { +: THREADSAFE + POINT_PROCESS MyExp2SynBB + RANGE tau1, tau2, e, i, g, Vwt, gmax + NONSPECIFIC_CURRENT i +} + +UNITS { + (nA) = (nanoamp) + (mV) = (millivolt) + (uS) = (microsiemens) +} + +PARAMETER { + tau1=.1 (ms) <1e-9,1e9> + tau2 = 10 (ms) <1e-9,1e9> + e=0 (mV) + gmax = 1e9 (uS) + Vwt = 0 : weight for inputs coming in from vector +} + +ASSIGNED { + v (mV) + i (nA) + g (uS) + factor + etime (ms) +} + +STATE { + A (uS) + B (uS) +} + +INITIAL { + LOCAL tp + + Vwt = 0 : testing + + if (tau1/tau2 > .9999) { + tau1 = .9999*tau2 + } + A = 0 + B = 0 + tp = (tau1*tau2)/(tau2 - tau1) * log(tau2/tau1) + factor = -exp(-tp/tau1) + exp(-tp/tau2) + factor = 1/factor +} + +BREAKPOINT { + SOLVE state METHOD cnexp + g = B - A + if (g>gmax) {g=gmax}: saturation + i = g*(v - e) +} + +DERIVATIVE state { + A' = -A/tau1 + B' = -B/tau2 +} + +NET_RECEIVE(w (uS)) {LOCAL ww + ww=w + A = A + ww*factor + B = B + ww*factor +} diff --git a/examples/CA3model_RasterColoredbyPhase/mod/MyExp2SynNMDABB.mod b/examples/CA3model_RasterColoredbyPhase/mod/MyExp2SynNMDABB.mod new file mode 100644 index 000000000..01291643a --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/MyExp2SynNMDABB.mod @@ -0,0 +1,108 @@ +: $Id: MyExp2SynNMDABB.mod,v 1.4 2010/12/13 21:28:02 samn Exp $ +NEURON { +: THREADSAFE + POINT_PROCESS MyExp2SynNMDABB + RANGE tau1, tau2, e, i, iNMDA, s, sNMDA, r, tau1NMDA, tau2NMDA, Vwt, smax, sNMDAmax + NONSPECIFIC_CURRENT i, iNMDA +} + +UNITS { + (nA) = (nanoamp) + (mV) = (millivolt) + (uS) = (microsiemens) +} + +PARAMETER { + tau1 = 0.1 (ms) <1e-9,1e9> + tau2 = 10 (ms) <1e-9,1e9> + tau1NMDA = 15 (ms) + tau2NMDA = 150 (ms) + e = 0 (mV) + mg = 1 + r = 1 + smax = 1e9 (1) + sNMDAmax = 1e9 (1) + + Vwt = 0 : weight for inputs coming in from vector +} + +ASSIGNED { + v (mV) + i (nA) + iNMDA (nA) + s (1) + sNMDA (1) + mgblock (1) + factor (1) + factor2 (1) + + etime (ms) +} + +STATE { + A (1) + B (1) + A2 (1) + B2 (1) +} + +INITIAL { + + LOCAL tp + + Vwt = 0 : testing + + if (tau1/tau2 > .9999) { + tau1 = .9999*tau2 + } + A = 0 + B = 0 + tp = (tau1*tau2)/(tau2 - tau1) * log(tau2/tau1) + factor = -exp(-tp/tau1) + exp(-tp/tau2) + factor = 1/factor + + if (tau1NMDA/tau2NMDA > .9999) { + tau1NMDA = .9999*tau2NMDA + } + A2 = 0 + B2 = 0 + tp = (tau1NMDA*tau2NMDA)/(tau2NMDA - tau1NMDA) * log(tau2NMDA/tau1NMDA) + factor2 = -exp(-tp/tau1NMDA) + exp(-tp/tau2NMDA) + factor2 = 1/factor2 +} + +BREAKPOINT { + SOLVE state METHOD cnexp + : Jahr Stevens 1990 J. Neurosci + mgblock = 1.0 / (1.0 + 0.28 * exp(-0.062(/mV) * v) ) + s = B - A + sNMDA = B2 - A2 + if (s >smax) {s =smax }: saturation + if (sNMDA>sNMDAmax) {sNMDA=sNMDAmax}: saturation + i = s * (v - e) + iNMDA = sNMDA * (v - e) * mgblock +} + +DERIVATIVE state { + A' = -A/tau1 + B' = -B/tau2 + A2' = -A2/tau1NMDA + B2' = -B2/tau2NMDA +} + +NET_RECEIVE(w (uS)) {LOCAL ww + ww=w + :printf("NMDA Spike: %g\n", t) + if(r>=0){ : if r>=0, g = AMPA + NMDA*r + A = A + factor *ww + B = B + factor *ww + A2 = A2 + factor2*ww*r + B2 = B2 + factor2*ww*r + }else{ + if(r>-1000){ : if r>-1, g = NMDA*r + A2 = A2 - factor2*ww*r + B2 = B2 - factor2*ww*r + } + : if r<0 and r<>-1, g = 0 + } +} diff --git a/examples/CA3model_RasterColoredbyPhase/mod/aux_fun.inc b/examples/CA3model_RasterColoredbyPhase/mod/aux_fun.inc new file mode 100644 index 000000000..ccb579afb --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/aux_fun.inc @@ -0,0 +1,43 @@ +: $Id: aux_fun.inc,v 1.1 2009/11/04 01:24:52 samn Exp $ +COMMENT + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE +// +// Copyright 2007, The University Of Pennsylvania +// School of Engineering & Applied Science. +// All rights reserved. +// For research use only; commercial use prohibited. +// Distribution without permission of Maciej T. Lazarewicz not permitted. +// mlazarew@seas.upenn.edu +// +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ENDCOMMENT + + + +:------------------------------------------------------------------- +FUNCTION fun1(v(mV),V0(mV),A(/ms),B(mV))(/ms) { + + fun1 = A*exp((v-V0)/B) +} + +FUNCTION fun2(v(mV),V0(mV),A(/ms),B(mV))(/ms) { + + fun2 = A/(exp((v-V0)/B)+1) +} + +FUNCTION fun3(v(mV),V0(mV),A(/ms),B(mV))(/ms) { + + if(fabs((v-V0)/B)<1e-6) { + :if(v==V0) { + fun3 = A*B/1(mV) * (1- 0.5 * (v-V0)/B) + } else { + fun3 = A/1(mV)*(v-V0)/(exp((v-V0)/B)-1) + } +} + +FUNCTION min(x,y) { if (x<=y){ min = x }else{ min = y } } +FUNCTION max(x,y) { if (x>=y){ max = x }else{ max = y } } diff --git a/examples/CA3model_RasterColoredbyPhase/mod/caolmw.mod b/examples/CA3model_RasterColoredbyPhase/mod/caolmw.mod new file mode 100644 index 000000000..3ea21a7ef --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/caolmw.mod @@ -0,0 +1,47 @@ +: $Id: caolmw.mod,v 1.2 2010/11/30 16:40:09 samn Exp $ +COMMENT + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE +// +// Copyright 2007, The University Of Pennsylvania +// School of Engineering & Applied Science. +// All rights reserved. +// For research use only; commercial use prohibited. +// Distribution without permission of Maciej T. Lazarewicz not permitted. +// mlazarew@seas.upenn.edu +// +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ENDCOMMENT + +UNITS { + (mollar) = (1/liter) + (M) = (mollar) + (mM) = (millimollar) + (mA) = (milliamp) + (mV) = (millivolt) + (mS) = (millisiemens) +} + +NEURON { + SUFFIX Caolmw + USEION ca READ ica, cai WRITE cai + RANGE alpha, tau +} + +PARAMETER { + alpha = 0.002 (cm2-M/mA-ms) + tau = 80 (ms) +} + +ASSIGNED { ica (mA/cm2) } + +INITIAL { cai = 0 } + +STATE { cai (mM) } + +BREAKPOINT { SOLVE states METHOD cnexp } + +DERIVATIVE states { cai' = -(1000) * alpha * ica - cai/tau } diff --git a/examples/CA3model_RasterColoredbyPhase/mod/icaolmw.mod b/examples/CA3model_RasterColoredbyPhase/mod/icaolmw.mod new file mode 100644 index 000000000..51112d099 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/icaolmw.mod @@ -0,0 +1,51 @@ +: $Id: icaolmw.mod,v 1.2 2010/11/30 16:44:13 samn Exp $ +COMMENT + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE +// +// Copyright 2007, The University Of Pennsylvania +// School of Engineering & Applied Science. +// All rights reserved. +// For research use only; commercial use prohibited. +// Distribution without permission of Maciej T. Lazarewicz not permitted. +// mlazarew@seas.upenn.edu +// +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ENDCOMMENT + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) + (mS) = (millisiemens) +} + +NEURON { + SUFFIX ICaolmw + USEION ca WRITE ica + RANGE gca,eca +} + +PARAMETER { + gca = 1 (mS/cm2) + eca = 120 (mV) +} + +ASSIGNED { + ica (mA/cm2) + v (mV) +} + +PROCEDURE iassign () { ica = (1e-3) * gca * mcainf(v)^2 * (v-eca) } + +INITIAL { + iassign() +} + +BREAKPOINT { iassign() } + +FUNCTION mcainf(v(mV)) { mcainf = fun2(v, -20, 1, -9)*1(ms) } + +INCLUDE "aux_fun.inc" diff --git a/examples/CA3model_RasterColoredbyPhase/mod/iholmw.mod b/examples/CA3model_RasterColoredbyPhase/mod/iholmw.mod new file mode 100644 index 000000000..ccd919202 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/iholmw.mod @@ -0,0 +1,60 @@ +: $Id: iholmw.mod,v 1.2 2010/11/30 16:34:22 samn Exp $ +COMMENT + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE +// +// Copyright 2007, The University Of Pennsylvania +// School of Engineering & Applied Science. +// All rights reserved. +// For research use only; commercial use prohibited. +// Distribution without permission of Maciej T. Lazarewicz not permitted. +// mlazarew@seas.upenn.edu +// +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ENDCOMMENT + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) + (mS) = (millisiemens) +} + +NEURON { + SUFFIX Iholmw + NONSPECIFIC_CURRENT i + RANGE gh,eh +} + +PARAMETER { + gh = 0.15 (mS/cm2) + eh = -40 (mV) +} + +ASSIGNED { + v (mV) + i (mA/cm2) +} + +STATE { q } + +PROCEDURE iassign () { i = (1e-3) * gh * q * (v-eh) } + +INITIAL { + q = qinf(v) + iassign() +} + +BREAKPOINT { + SOLVE states METHOD cnexp + iassign() +} + +DERIVATIVE states { q' = (qinf(v)-q)/qtau(v) } + +FUNCTION qinf(v(mV)) { qinf = fun2(v, -80, 1, 10)*1(ms) } +FUNCTION qtau(v(mV))(ms) { qtau = 200(ms)/(exp((v+70(mV))/20(mV))+exp(-(v+70(mV))/20(mV))) + 5(ms) } + +INCLUDE "aux_fun.inc" diff --git a/examples/CA3model_RasterColoredbyPhase/mod/kcaolmw.mod b/examples/CA3model_RasterColoredbyPhase/mod/kcaolmw.mod new file mode 100644 index 000000000..b2368787e --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/kcaolmw.mod @@ -0,0 +1,52 @@ +: $Id: kcaolmw.mod,v 1.2 2010/11/30 16:47:18 samn Exp $ +COMMENT + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE +// +// Copyright 2007, The University Of Pennsylvania +// School of Engineering & Applied Science. +// All rights reserved. +// For research use only; commercial use prohibited. +// Distribution without permission of Maciej T. Lazarewicz not permitted. +// mlazarew@seas.upenn.edu +// +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ENDCOMMENT + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) + (mS) = (millisiemens) + (mollar) = (1/liter) + (mM) = (millimollar) +} + +NEURON { + SUFFIX KCaolmw + USEION k WRITE ik + USEION ca READ cai + RANGE gkca,ek,kd +} + +PARAMETER { + gkca = 10 (mS/cm2) + ek = -90 (mV) + kd = 30 (mM) +} + +ASSIGNED { + cai (mM) + v (mV) + ik (mA/cm2) +} + +PROCEDURE iassign () { ik = (1e-3) * gkca * cai/(cai+kd) * (v-ek) } + +INITIAL { + iassign() +} + +BREAKPOINT { iassign() } diff --git a/examples/CA3model_RasterColoredbyPhase/mod/kdrbwb.mod b/examples/CA3model_RasterColoredbyPhase/mod/kdrbwb.mod new file mode 100644 index 000000000..fc52ae534 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/kdrbwb.mod @@ -0,0 +1,76 @@ +: $Id: kdrbwb.mod,v 1.4 2010/12/13 21:35:26 samn Exp $ +COMMENT + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE +// +// Copyright 2007, The University Of Pennsylvania +// School of Engineering & Applied Science. +// All rights reserved. +// For research use only; commercial use prohibited. +// Distribution without permission of Maciej T. Lazarewicz not permitted. +// mlazarew@seas.upenn.edu +// +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ENDCOMMENT + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) + (mS) = (millisiemens) +} + +NEURON { + SUFFIX Kdrbwb + USEION k WRITE ik + RANGE phin,gkdr,ek + RANGE taon,ninf +} + +PARAMETER { + gkdr = 9 (mS/cm2) + ek = -90 (mV) + phin = 5 +} + +ASSIGNED { + v (mV) + ik (mA/cm2) + celsius (degC) + ninf (1) + taon (ms) +} + +STATE { n } + +PROCEDURE iassign () { ik = (1e-3) * gkdr * n^4 * (v-ek) } + +INITIAL { + rates(v) + n = ninf + iassign() +} + +BREAKPOINT { + SOLVE states METHOD cnexp + iassign() +} + +DERIVATIVE states { + rates(v) + n' = (ninf-n)/taon +} + +PROCEDURE rates(v(mV)) { LOCAL an, bn, q10 + q10 = phin:^((celsius-27.0(degC))/10.0(degC)) + + an = fun3(v, -34, -0.01, -10) + bn = fun1(v, -44, 0.125, -80) + + ninf = an/(an+bn) + taon = 1./((an+bn)*q10) +} + +INCLUDE "aux_fun.inc" diff --git a/examples/CA3model_RasterColoredbyPhase/mod/nafbwb.mod b/examples/CA3model_RasterColoredbyPhase/mod/nafbwb.mod new file mode 100644 index 000000000..37281dc94 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/nafbwb.mod @@ -0,0 +1,81 @@ +: $Id: nafbwb.mod,v 1.4 2010/12/13 21:35:08 samn Exp $ +COMMENT + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE +// +// Copyright 2007, The University Of Pennsylvania +// School of Engineering & Applied Science. +// All rights reserved. +// For research use only; commercial use prohibited. +// Distribution without permission of Maciej T. Lazarewicz not permitted. +// mlazarew@seas.upenn.edu +// +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ENDCOMMENT + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) + (mS) = (millisiemens) +} + +NEURON { + SUFFIX Nafbwb + USEION na WRITE ina + RANGE phih + RANGE gna, ena, taoh : testing +} + +PARAMETER { + gna = 35 (mS/cm2) + ena = 55 (mV) + phih = 5 +} + +ASSIGNED { + v (mV) + ina (mA/cm2) + minf (1) + hinf (1) + taoh (ms) + celsius (degC) +} + +STATE { h } + +PROCEDURE iassign () { ina = (1e-3) * gna * minf^3 * h * (v-ena) } + +INITIAL { + rates(v) + h = hinf + iassign() +} + +BREAKPOINT { + SOLVE states METHOD cnexp + iassign() +} + +DERIVATIVE states { + rates(v) + h' = (hinf-h)/taoh +} + +PROCEDURE rates(v(mV)) { LOCAL am, bm, ah, bh, q10 + + q10 = phih:^((celsius-27.0(degC))/10.0(degC)) + + am = fun3(v, -35, -0.1, -10) + bm = fun1(v, -60, 4, -18) + minf = am/(am+bm) + + ah = fun1(v, -58, 0.07, -20) + bh = fun2(v, -28, 1, -10) + hinf = ah/(ah+bh) + taoh = 1./((ah+bh)*q10) +} + +INCLUDE "aux_fun.inc" diff --git a/examples/CA3model_RasterColoredbyPhase/src/cfg.py b/examples/CA3model_RasterColoredbyPhase/src/cfg.py new file mode 100644 index 000000000..85483afd6 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/src/cfg.py @@ -0,0 +1,34 @@ +from netpyne import specs + +cfg = specs.SimConfig() + +cfg.duration = 750 +cfg.dt = 0.1 +cfg.hparams = {'v_init': -65.0} +cfg.verbose = False +cfg.recordTraces = {'V_soma':{'sec':'soma','loc':0.5,'var':'v'}} # Dict with traces to record +cfg.recordStim = False +cfg.recordStep = 0.1 # Step size in ms to save data (eg. V traces, LFP, etc) +cfg.filename = '00' # Set file output name +cfg.savePickle = False # Save params, network and sim output to pickle file +cfg.saveDat = False +cfg.printRunTime = 0.1 +cfg.recordLFP = [[50, 50, 50]] + +cfg.analysis['plotRaster']={ + 'colorbyPhase':{ +# 'signal': LFP_array, # when signal is provided as a np.array +# 'signal': 'LFP_signal.pkl', # when signal is provided as a list in a .pkl +# 'fs': 10000, # in both cases, sampling frequency would be neccessary + 'signal': 'LFP', # internal signal, from the computation of LFPs + 'electrode':1, + 'filtFreq':[4,8], + 'filtOrder': 3, + 'pop_background': True, + 'include_signal': True + }, + 'include':['allCells'], + 'saveFig':True, 'timeRange': [100,600]} # Plot a raster +cfg.analysis['plotTraces'] = {'include': [0, 1, 800, 801, 1000, 1001], 'saveFig': True} # Plot recorded traces for this list of cells +cfg.analysis['plotLFPTimeSeries'] = {'showFig': True, 'saveFig': True} +#cfg.analysis['plotShape'] = {'includePre': ['all'],'includePost': [0,800,1000],'cvar':'numSyns','dist':0.7, 'saveFig': True} diff --git a/examples/CA3model_RasterColoredbyPhase/src/init.py b/examples/CA3model_RasterColoredbyPhase/src/init.py new file mode 100644 index 000000000..5c371b5c4 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/src/init.py @@ -0,0 +1,10 @@ +""" +init.py + +Starting script to run NetPyNE-based CA3 model. +""" + +from netpyne import sim + +cfg, netParams = sim.loadFromIndexFile('index.npjson') +sim.createSimulateAnalyze(netParams, cfg) diff --git a/examples/CA3model_RasterColoredbyPhase/src/netParams.py b/examples/CA3model_RasterColoredbyPhase/src/netParams.py new file mode 100644 index 000000000..083a02dde --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/src/netParams.py @@ -0,0 +1,322 @@ +from netpyne import specs +try: + from __main__ import cfg +except: + from cfg import cfg + +# Network parameters +netParams = specs.NetParams() # object of class NetParams to store the network parameters +netParams.defaultThreshold = 0.0 +netParams.defineCellShapes = True # sets 3d geometry aligned along the y-axis + + +############################################################################### +## Cell types +############################################################################### +# Basket cell +BasketCell = {'secs':{}} +BasketCell['secs']['soma'] = {'geom': {}, 'mechs': {}} +BasketCell['secs']['soma']['geom'] = {'diam': 100, 'L': 31.831, 'nseg': 1, 'cm': 1} +BasketCell['secs']['soma']['mechs'] = {'pas': {'g': 0.1e-3, 'e': -65}, 'Nafbwb': {}, 'Kdrbwb': {}} +netParams.cellParams['BasketCell'] = BasketCell + + +# OLM cell +OlmCell = {'secs':{}} +OlmCell['secs']['soma'] = {'geom': {}, 'mechs': {}} +OlmCell['secs']['soma']['geom'] = {'diam': 100, 'L': 31.831, 'nseg': 1, 'cm': 1} +OlmCell['secs']['soma']['mechs'] = { + 'pas': {'g': 0.1e-3, 'e': -65}, + 'Nafbwb': {}, + 'Kdrbwb': {}, + 'Iholmw': {}, + 'Caolmw': {}, + 'ICaolmw': {}, + 'KCaolmw': {}} +netParams.cellParams['OlmCell'] = OlmCell + + +# Pyramidal cell +PyrCell = {'secs':{}} +PyrCell['secs']['soma'] = {'geom': {}, 'mechs': {}} +PyrCell['secs']['soma']['geom'] = {'diam': 20, 'L': 20, 'cm': 1, 'Ra': 150} +PyrCell['secs']['soma']['mechs'] = { + 'pas': {'g': 0.0000357, 'e': -70}, + 'nacurrent': {}, + 'kacurrent': {}, + 'kdrcurrent': {}, + 'hcurrent': {}} +PyrCell['secs']['Bdend'] = {'geom': {}, 'mechs': {}} +PyrCell['secs']['Bdend']['geom'] = {'diam': 2, 'L': 200, 'cm': 1, 'Ra': 150} +PyrCell['secs']['Bdend']['topol'] = {'parentSec': 'soma', 'parentX': 0, 'childX': 0} +PyrCell['secs']['Bdend']['mechs'] = { + 'pas': {'g': 0.0000357, 'e': -70}, + 'nacurrent': {'ki': 1}, + 'kacurrent': {}, + 'kdrcurrent': {}, + 'hcurrent': {}} +PyrCell['secs']['Adend1'] = {'geom': {}, 'mechs': {}} +PyrCell['secs']['Adend1']['geom'] = {'diam': 2, 'L': 150, 'cm': 1, 'Ra': 150} +PyrCell['secs']['Adend1']['topol'] = {'parentSec': 'soma', 'parentX': 1.0, 'childX': 0} # here there is a change: connected to end soma(1) instead of soma(0.5) +PyrCell['secs']['Adend1']['mechs'] = { + 'pas': {'g': 0.0000357, 'e': -70}, + 'nacurrent': {'ki': 0.5}, + 'kacurrent': {'g': 0.072}, + 'kdrcurrent': {}, + 'hcurrent': {'v50': -82, 'g': 0.0002}} +PyrCell['secs']['Adend2'] = {'geom': {}, 'mechs': {}} +PyrCell['secs']['Adend2']['geom'] = {'diam': 2, 'L': 150, 'cm': 1, 'Ra': 150} +PyrCell['secs']['Adend2']['topol'] = {'parentSec': 'Adend1', 'parentX': 1, 'childX': 0} +PyrCell['secs']['Adend2']['mechs'] = { + 'pas': {'g': 0.0000357, 'e': -70}, + 'nacurrent': {'ki': 0.5}, + 'kacurrent': {'g': 0, 'gd': 0.120}, + 'kdrcurrent': {}, + 'hcurrent': {'v50': -90, 'g': 0.0004}} +PyrCell['secs']['Adend3'] = {'geom': {}, 'mechs': {}} +PyrCell['secs']['Adend3']['geom'] = {'diam': 2, 'L': 150, 'cm': 2, 'Ra': 150} +PyrCell['secs']['Adend3']['topol'] = {'parentSec': 'Adend2', 'parentX': 1, 'childX': 0} +PyrCell['secs']['Adend3']['mechs'] = { + 'pas': {'g': 0.0000714, 'e': -70}, + 'nacurrent': {'ki': 0.5}, + 'kacurrent': {'g': 0, 'gd': 0.200}, + 'kdrcurrent': {}, + 'hcurrent': {'v50': -90, 'g': 0.0007}} +netParams.cellParams['PyrCell'] = PyrCell + + +############################################################################### +## Synaptic mechs +############################################################################### + +netParams.synMechParams['AMPAf'] = {'mod': 'MyExp2SynBB', 'tau1': 0.05, 'tau2': 5.3, 'e': 0} +netParams.synMechParams['NMDA'] = {'mod': 'MyExp2SynNMDABB', 'tau1': 0.05, 'tau2': 5.3, 'tau1NMDA': 15, 'tau2NMDA': 150, 'r': 1, 'e': 0} +netParams.synMechParams['GABAf'] = {'mod': 'MyExp2SynBB', 'tau1': 0.07, 'tau2': 9.1, 'e': -80} +netParams.synMechParams['GABAs'] = {'mod': 'MyExp2SynBB', 'tau1': 0.2, 'tau2': 20, 'e': -80} +netParams.synMechParams['GABAss'] = {'mod': 'MyExp2SynBB', 'tau1': 20, 'tau2': 40, 'e': -80} + + +############################################################################### +## Populations +############################################################################### +netParams.popParams['PYR'] = {'cellType': 'PyrCell', 'numCells': 800} +netParams.popParams['BC'] = {'cellType': 'BasketCell', 'numCells': 200} +netParams.popParams['OLM'] = {'cellType': 'OlmCell', 'numCells': 200} + + +############################################################################### +# Current-clamp to cells +############################################################################### +netParams.stimSourceParams['IClamp_PYR'] = {'type': 'IClamp', 'del': 2*cfg.dt, 'dur': 1e9, 'amp': 50e-3} +netParams.stimSourceParams['IClamp_OLM'] = {'type': 'IClamp', 'del': 2*cfg.dt, 'dur': 1e9, 'amp': -25e-3} + +netParams.stimTargetParams['IClamp_PYR->PYR'] = { + 'source': 'IClamp_PYR', + 'sec': 'soma', + 'loc': 0.5, + 'conds': {'pop': 'PYR'}} + +netParams.stimTargetParams['IClamp_OLM->OLM'] = { + 'source': 'IClamp_OLM', + 'sec': 'soma', + 'loc': 0.5, + 'conds': {'pop': 'OLM'}} + + +############################################################################### +# Setting connections +############################################################################### + +# PYR -> X, NMDA +netParams.connParams['PYR->BC_NMDA'] = {'preConds': {'pop': 'PYR'}, 'postConds': {'pop': 'BC'}, + 'convergence': 100, + 'weight': 1.38e-3, + 'delay': 2, + 'sec': 'soma', + 'loc': 0.5, + 'synMech': 'NMDA'} + +netParams.connParams['PYR->OLM_NMDA'] = {'preConds': {'pop': 'PYR'}, 'postConds': {'pop': 'OLM'}, + 'convergence': 10, + 'weight': 0.7e-3, + 'delay': 2, + 'sec': 'soma', + 'loc': 0.5, + 'synMech': 'NMDA'} + +netParams.connParams['PYR->PYR_NMDA'] = {'preConds': {'pop': 'PYR'}, 'postConds': {'pop': 'PYR'}, + 'convergence': 25, + 'weight': 0.004e-3, + 'delay': 2, + 'sec': 'Bdend', + 'loc': 1.0, + 'synMech': 'NMDA'} + +# PYR -> X, AMPA +netParams.connParams['PYR->BC_AMPA'] = {'preConds': {'pop': 'PYR'}, 'postConds': {'pop': 'BC'}, + 'convergence': 100, + 'weight': 0.36e-3, + 'delay': 2, + 'sec': 'soma', + 'loc': 0.5, + 'synMech': 'AMPAf'} + +netParams.connParams['PYR->OLM_AMPA'] = {'preConds': {'pop': 'PYR'}, 'postConds': {'pop': 'OLM'}, + 'convergence': 10, + 'weight': 0.36e-3, + 'delay': 2, + 'sec': 'soma', + 'loc': 0.5, + 'synMech': 'AMPAf'} + +netParams.connParams['PYR->PYR_AMPA'] = {'preConds': {'pop': 'PYR'}, 'postConds': {'pop': 'PYR'}, + 'convergence': 25, + 'weight': 0.02e-3, + 'delay': 2, + 'sec': 'Bdend', + 'loc': 1.0, + 'synMech': 'AMPAf'} + +# BC -> X, GABA +netParams.connParams['BC->BC_GABA'] = {'preConds': {'pop': 'BC'}, 'postConds': {'pop': 'BC'}, + 'convergence': 60, + 'weight':4.5e-3, + 'delay': 2, + 'sec': 'soma', + 'loc': 0.5, + 'synMech': 'GABAf'} + +netParams.connParams['BC->PYR_GABA'] = {'preConds': {'pop': 'BC'}, 'postConds': {'pop': 'PYR'}, + 'convergence': 50, + 'weight': 0.72e-3, + 'delay': 2, + 'sec': 'soma', + 'loc': 0.5, + 'synMech': 'GABAf'} + + +# OLM -> PYR, GABA +netParams.connParams['OLM->PYR_GABA'] = {'preConds': {'pop': 'OLM'}, 'postConds': {'pop': 'PYR'}, + 'convergence': 20, + 'weight': 72e-3, + 'delay': 2, + 'sec': 'Adend2', + 'loc': 0.5, + 'synMech': 'GABAs'} + + +############################################################################### +# Setting NetStims +############################################################################### +# to PYR +netParams.stimSourceParams['NetStim_PYR_SOMA_AMPA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_PYR_SOMA_AMPA->PYR'] = { + 'source': 'NetStim_PYR_SOMA_AMPA', + 'conds': {'pop': 'PYR'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 4*0.05e-3, # different from published value + 'delay': 2*cfg.dt, + 'synMech': 'AMPAf'} + +netParams.stimSourceParams['NetStim_PYR_ADEND3_AMPA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_PYR_ADEND3_AMPA->PYR'] = { + 'source': 'NetStim_PYR_ADEND3_AMPA', + 'conds': {'pop': 'PYR'}, + 'sec': 'Adend3', + 'loc': 0.5, + 'weight': 4*0.05e-3, # different from published value + 'delay': 2*cfg.dt, + 'synMech': 'AMPAf'} + +netParams.stimSourceParams['NetStim_PYR_SOMA_GABA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_PYR_SOMA_GABA->PYR'] = { + 'source': 'NetStim_PYR_SOMA_GABA', + 'conds': {'pop': 'PYR'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 0.012e-3, + 'delay': 2*cfg.dt, + 'synMech': 'GABAf'} + +netParams.stimSourceParams['NetStim_PYR_ADEND3_GABA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_PYR_ADEND3_GABA->PYR'] = { + 'source': 'NetStim_PYR_ADEND3_GABA', + 'conds': {'pop': 'PYR'}, + 'sec': 'Adend3', + 'loc': 0.5, + 'weight': 0.012e-3, + 'delay': 2*cfg.dt, + 'synMech': 'GABAf'} + +netParams.stimSourceParams['NetStim_PYR_ADEND3_NMDA'] = {'type': 'NetStim', 'interval': 100, 'number': int((1000/100.0)*cfg.duration), 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_PYR_ADEND3_NMDA->PYR'] = { + 'source': 'NetStim_PYR_ADEND3_NMDA', + 'conds': {'pop': 'PYR'}, + 'sec': 'Adend3', + 'loc': 0.5, + 'weight': 6.5e-3, + 'delay': 2*cfg.dt, + 'synMech': 'NMDA'} + +# to BC +netParams.stimSourceParams['NetStim_BC_SOMA_AMPA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_BC_SOMA_AMPA->BC'] = { + 'source': 'NetStim_BC_SOMA_AMPA', + 'conds': {'pop': 'BC'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 0.02e-3, + 'delay': 2*cfg.dt, + 'synMech': 'AMPAf'} + +netParams.stimSourceParams['NetStim_BC_SOMA_GABA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_BC_SOMA_GABA->BC'] = { + 'source': 'NetStim_BC_SOMA_GABA', + 'conds': {'pop': 'BC'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 0.2e-3, + 'delay': 2*cfg.dt, + 'synMech': 'GABAf'} + +# to OLM +netParams.stimSourceParams['NetStim_OLM_SOMA_AMPA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_OLM_SOMA_AMPA->OLM'] = { + 'source': 'NetStim_OLM_SOMA_AMPA', + 'conds': {'pop': 'OLM'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 0.0625e-3, + 'delay': 2*cfg.dt, + 'synMech': 'AMPAf'} + +netParams.stimSourceParams['NetStim_OLM_SOMA_GABA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_OLM_SOMA_GABA->OLM'] = { + 'source': 'NetStim_OLM_SOMA_GABA', + 'conds': {'pop': 'OLM'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 0.2e-3, + 'delay': 2*cfg.dt, + 'synMech': 'GABAf'} + +# Medial Septal inputs to BC and OLM cells +netParams.stimSourceParams['Septal'] = {'type': 'NetStim', 'interval': 150, 'number': int((1000/150)*cfg.duration), 'start': 0, 'noise': 0} +netParams.stimTargetParams['Septal->BC'] = { + 'source': 'Septal', + 'conds': {'pop': 'BC'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 1.6e-3, + 'delay': 2*cfg.dt, + 'synMech': 'GABAss'} + +netParams.stimTargetParams['Septal->OLM'] = { + 'source': 'Septal', + 'conds': {'pop': 'OLM'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 1.6e-3, + 'delay': 2*cfg.dt, + 'synMech': 'GABAss'} diff --git a/netpyne/analysis/spikes.py b/netpyne/analysis/spikes.py index bfc510199..39bb9c403 100644 --- a/netpyne/analysis/spikes.py +++ b/netpyne/analysis/spikes.py @@ -49,7 +49,7 @@ def prepareSpikeData( fileDesc=None, fileType=None, fileDir=None, - calculatePhase=False, + colorbyPhase=None, **kwargs ): """ @@ -157,6 +157,105 @@ def prepareSpikeData( sel = pd.concat([sel, ns]) numNetStims += len(spkindsNew) + # Calculating Phase of spikes + if isinstance(colorbyPhase,dict): + + try: + Signal = colorbyPhase['signal'] + except: + print("Importing signal for coloring spikes - No information about the signal") + Signal = 'LFP' + + if isinstance(Signal, basestring) or isinstance(Signal,np.ndarray): + from scipy import signal, stats, interpolate + from scipy.signal import hilbert + from math import fmod, pi + + rawSignal = [] + if isinstance(Signal, basestring): + # signal provided as a list packed in a pkl + if Signal.endswith('.pkl'): + import pickle + + with open(Signal, 'rb') as input_file: + rawSignal = pickle.load(input_file) + + try: + fs = colorbyPhase['fs'] + except: + print("Importing signal for coloring spikes - No frequency sampling provided") + fs = 1000.0 # assumes data sampled in ms + + time = np.linspace(0,len(rawSignal)*1000/fs,len(rawSignal)+1) # in milliseconds + + elif Signal == 'LFP': + try: + electrode = colorbyPhase['electrode'] + if electrode > sim.net.recXElectrode.nsites: + print('Wrong electrode number for coloring spikes according to LFP phase - Assigning first element in LFP recording setup') + electrode = 1 + except: + electrode = 1 + + rawSignal = [sim.allSimData['LFP'][n][electrode-1] for n in range(len(sim.allSimData['LFP']))] + fs = 1000.0/sim.cfg.recordStep + time = np.linspace(0,len(rawSignal)*1000/fs,len(rawSignal)+1) # in milliseconds + + else: + print('No signal recovered to color spikes according to its phase') + + # it is an array + else: + rawSignal = Signal.tolist() + try: + fs = colorbyPhase['fs'] + except: + print("Importing signal for coloring spikes - No frequency sampling provided") + fs = 1000.0 # assumes data sampled in ms + + time = np.linspace(0,len(rawSignal)*1000/fs,len(rawSignal)+1) # in milliseconds + + # Processing rawSignal + rawSignal.append(2*rawSignal[-1]-rawSignal[-2]) # extrapolation for the last element + rawSignal = stats.zscore(np.float32(rawSignal)) + rawSignal_ = np.r_[rawSignal[-1::-1],rawSignal,rawSignal[-1::-1]] # Reflect signal to minimize edge artifacts + + nyquist = fs/2.0 + + # parameters provided to filter the signal - setting defaults otherwise + try: + filtOrder = colorbyPhase['filtOrder'] + except: + filtOrder = 3 + + try: + filtFreq = colorbyPhase['filtFreq'] + except: + filtFreq = [1,500] + + if isinstance(filtFreq, list): # bandpass + Wn = [filtFreq[0]/nyquist, filtFreq[1]/nyquist] + b, a = signal.butter(filtOrder, Wn, btype='bandpass') + + elif isinstance(filtFreq, Number): # lowpass + Wn = filtFreq/nyquist + b, a = signal.butter(filtOrder, Wn) + + rawSignalFiltered_ = signal.filtfilt(b, a, rawSignal_) + + analytic_signal = hilbert(rawSignalFiltered_)[len(rawSignal):-len(rawSignal)] + amplitude_envelope = np.abs(analytic_signal) + instantaneous_phase = np.unwrap(np.angle(analytic_signal)) + instantaneous_phase_mod = [(fmod(instantaneous_phase[nn]+pi,2*pi)-pi)*(180/pi) for nn in range(len(instantaneous_phase))] + instantaneous_phase = np.r_[instantaneous_phase_mod] + + f_Rhythm = interpolate.interp1d(time,instantaneous_phase) + + sel['spkPhase'] = sel['spkt'].apply(f_Rhythm) + + else: + print('No signal recovered to color spikes according to its phase') + if len(cellGids) > 0 and numNetStims: ylabelText = ylabelText + ' and NetStims (at the end)' elif numNetStims: @@ -277,6 +376,12 @@ def prepareSpikeData( 'axisArgs': axisArgs, 'legendLabels': legendLabels, } + + if colorbyPhase: + spikeData.update({'spkPhases': sel['spkPhase'].tolist()}) + if 'include_signal' in colorbyPhase and colorbyPhase['include_signal']==True: + spikeData.update({'signal': analytic_signal}) + spikeData.update({'time': time}) if saveData: saveFigData(spikeData, fileName=fileName, fileDesc='spike_data', fileType=fileType, fileDir=fileDir, sim=sim) @@ -292,6 +397,7 @@ def prepareRaster( maxSpikes=1e8, orderBy='gid', popRates=True, + colorbyPhase=None, saveData=False, fileName=None, fileDesc=None, @@ -311,6 +417,7 @@ def prepareRaster( maxSpikes=maxSpikes, orderBy=orderBy, popRates=popRates, + colorbyPhase=colorbyPhase, saveData=saveData, fileName=fileName, fileDesc=fileDesc if fileDesc else 'raster_data', diff --git a/netpyne/plotting/plotRaster.py b/netpyne/plotting/plotRaster.py index 9d37eb8f4..a5be18c9a 100644 --- a/netpyne/plotting/plotRaster.py +++ b/netpyne/plotting/plotRaster.py @@ -18,6 +18,7 @@ def plotRaster( popLabels=None, popColors=None, syncLines=False, + colorbyPhase = None, legend=True, colorList=None, orderInverse=False, @@ -111,6 +112,27 @@ def plotRaster( *Default:* ``False`` + colorbyPhase : dict + Dictionary specifying conditions to plot spikes colored by the phase of a simultaneous signal, filtered in a given range + + *Default:* ``None`` colors spikes according to other options (by populations) + + *Dictionary entries:* + + ``'signal'`` specifies the signal. Options are: ``'LFP'``, which takes the signal from the local fiel potential generated in the ongoing simulation, a numpy array of scalars (for example, an external signal used for stimulation), or an external pickle file, + + ``'fs'`` is the sampling frequency, which should be specified when the signal is obtained from external sources (pickle file or numpy array). Otherwise, it is assumed to be 1000 Hz. If the signal is specified by ``'LFP'``, then the sampling rate is obtained from the internal simulation (cfg.recordStep), + + ``'electrode'`` selects the electrode from the LFP setup. Default is electrode 1, + + ``'filtFreq'`` is a list specifying the range for filtering the signal (band-pass). For example, ``[4,8]`` to select theta rhythm. The default is a very broadband filtering (essentially, the raw signal) ``[1,500]``, + + ``'filtOrder'`` is the filter order (Butterworth) to process the signal, + + ``'pop_background'`` is a boolean option to color each population alternately with a gray background, for better visualization. The default is False, + + ``'include_signal'`` is a boolean option to plot the filtered signal below the raster plot. The default is False. + legend : bool Whether or not to add a legend to the plot. @@ -193,7 +215,8 @@ def plotRaster( sim = kwargs['sim'] rasterData = sim.analysis.prepareRaster( - timeRange=timeRange, maxSpikes=maxSpikes, orderBy=orderBy, popRates=popRates, **kwargs + timeRange=timeRange, maxSpikes=maxSpikes, orderBy=orderBy, + popRates=popRates, colorbyPhase=colorbyPhase, **kwargs ) print('Plotting raster...') @@ -208,6 +231,8 @@ def plotRaster( spkTimes = rasterData['spkTimes'] spkInds = rasterData['spkInds'] spkGids = rasterData['spkGids'] + if colorbyPhase: + spkPhases = rasterData['spkPhases'] if not popNumCells: popNumCells = rasterData.get('popNumCells') @@ -288,6 +313,16 @@ def plotRaster( else: timeRange = [0, np.ceil(max(spkTimes))] + # Set features for raster plot colored by phase + if colorbyPhase: + spkColors = spkPhases + legend = False + kwargs['colorbar'] = {'vmin': -180, 'vmax': 180} + kwargs['background'] = False + if 'pop_background' in colorbyPhase: + if colorbyPhase['pop_background'] == True: + kwargs['background'] = {'popLabels': popLabels, 'popNumCells': popNumCells, 'timeRange': timeRange} + # Create a dictionary with the inputs for a scatter plot scatterData = {} scatterData['x'] = spkTimes @@ -297,10 +332,16 @@ def plotRaster( scatterData['marker'] = '|' scatterData['markersize'] = 5 scatterData['linewidth'] = 2 - scatterData['cmap'] = None scatterData['norm'] = None scatterData['alpha'] = None scatterData['linewidths'] = None + scatterData['cmap'] = None + if colorbyPhase: + scatterData['cmap'] = 'hsv' + if 'include_signal' in colorbyPhase and colorbyPhase['include_signal']==True: + scatterData['time'] = rasterData.get('time') + scatterData['signal'] = rasterData.get('signal') + scatterData['timeRange'] = timeRange # If a kwarg matches a scatter input key, use the kwarg value instead of the default for kwarg in list(kwargs.keys()): @@ -326,7 +367,10 @@ def plotRaster( kwargs.pop(kwarg) # create Plotter object - rasterPlotter = ScatterPlotter(data=scatterData, kind='raster', axis=axis, **axisArgs, **kwargs) + if 'signal' in scatterData: + rasterPlotter = ScatterPlotter(data=scatterData, kind='raster&signal', axis=axis, **axisArgs, **kwargs) + else: + rasterPlotter = ScatterPlotter(data=scatterData, kind='raster', axis=axis, **axisArgs, **kwargs) metaFig = rasterPlotter.metafig # add spike lines diff --git a/netpyne/plotting/plotter.py b/netpyne/plotting/plotter.py index 82c45b501..be967d265 100644 --- a/netpyne/plotting/plotter.py +++ b/netpyne/plotting/plotter.py @@ -127,13 +127,26 @@ def __init__(self, kind, sim=None, subplots=None, sharex=False, sharey=False, au else: dpi = self.rcParams['figure.dpi'] + if 'constrained_layout' in kwargs: + constrained_layout = kwargs['constrained_layout'] + else: + constrained_layout = False + if autosize: maxplots = np.max([nrows, ncols]) figSize0 = figSize[0] + (maxplots - 1) * (figSize[0] * autosize) figSize1 = figSize[1] + (maxplots - 1) * (figSize[1] * autosize) figSize = [figSize0, figSize1] - self.fig, self.ax = plt.subplots(nrows, ncols, sharex=sharex, sharey=sharey, figsize=figSize, dpi=dpi) + gridspec_kw = None +# if 'height_ratios' in kwargs: +# gridspec_kw = {'height_ratios': kwargs['height_ratios'], 'right': 0.3} + if 'gridspec_kw' in kwargs: + gridspec_kw = kwargs['gridspec_kw'] + + self.fig, self.ax = plt.subplots(nrows, ncols, sharex=sharex, sharey=sharey, + figsize=figSize, dpi=dpi, gridspec_kw=gridspec_kw, + constrained_layout=constrained_layout) # Add a metafig attribute to the figure self.fig.metafig = self @@ -205,6 +218,9 @@ def saveFig(self, sim=None, fileName=None, fileDesc=True, fileType='png', fileDi if '.' in saveFig: fileName, fileType = os.path.splitext(saveFig) fileType = fileType[1:] # drop the dot + elif saveFig == 'movie': + from neuron import h + fileName = sim.cfg.filename + '_shape_movie_' + str(round(h.t, 1)) + '.png' else: fileName = saveFig @@ -490,7 +506,16 @@ def __init__(self, data, kind, axis=None, twinx=False, twiny=False, sim=None, me # If an axis is input, plot there; otherwise make a new figure and axis if self.axis is None: if self.metafig is None: - self.metafig = MetaFigure(kind=self.kind, **kwargs) + if self.kind == 'raster&signal': + kwargs.update({'constrained_layout': True}) + kwargs.update({'gridspec_kw': {'height_ratios': [2,1], + 'right': 0.2}}) + # 'left':0.2, + # 'top':0.3, + # 'bottom':0.2}}) + self.metafig = MetaFigure(kind=self.kind, subplots=2, sharex=True, **kwargs) + else: + self.metafig = MetaFigure(kind=self.kind, **kwargs) self.fig = self.metafig.fig self.axis = self.metafig.ax else: @@ -561,11 +586,14 @@ def saveData(self, fileName=None, fileDesc=None, fileType=None, fileDir=None, si self.data, fileName=fileName, fileDesc=fileDesc, fileType=fileType, fileDir=fileDir, sim=sim, **kwargs ) - def formatAxis(self, **kwargs): + def formatAxis(self, axis=None, **kwargs): """Method to format the axis Parameters ---------- + axis : None or object + the axis to format + title : str Title to add to the axis. @@ -589,33 +617,39 @@ def formatAxis(self, **kwargs): Whether to invert the y axis. """ + curAx = axis + if curAx==None: + curAx = self.axis if 'title' in kwargs: - self.axis.set_title(kwargs['title']) + curAx.set_title(kwargs['title']) if 'xlabel' in kwargs: - self.axis.set_xlabel(kwargs['xlabel']) + curAx.set_xlabel(kwargs['xlabel']) if 'ylabel' in kwargs: - self.axis.set_ylabel(kwargs['ylabel']) + curAx.set_ylabel(kwargs['ylabel']) if 'xlim' in kwargs: if kwargs['xlim'] is not None: - self.axis.set_xlim(kwargs['xlim']) + curAx.set_xlim(kwargs['xlim']) if 'ylim' in kwargs: if kwargs['ylim'] is not None: - self.axis.set_ylim(kwargs['ylim']) + curAx.set_ylim(kwargs['ylim']) if 'invert_yaxis' in kwargs: if kwargs['invert_yaxis'] is True: - self.axis.invert_yaxis() + curAx.invert_yaxis() def addLegend(self, handles=None, labels=None, **kwargs): """Method to add a legend to the axis Parameters ---------- + axis : None or object + the axis to add the legends + handles : list List of Matplotlib legend handles. @@ -686,6 +720,7 @@ def addLegend(self, handles=None, labels=None, **kwargs): def addScalebar( self, + axis=None, matchx=True, matchy=True, hidex=True, @@ -703,6 +738,9 @@ def addScalebar( Parameters ---------- + axis : None or object + the axis to add the scalebar + matchx : bool If True, set size of scale bar to spacing between ticks, if False, set size using sizex params. @@ -766,8 +804,12 @@ def addScalebar( """ + curAx = axis + if curAx==None: + curAx = self.axis + _add_scalebar( - self.axis, + curAx, matchx=matchx, matchy=matchy, hidex=hidex, @@ -782,17 +824,67 @@ def addScalebar( **kwargs ) - def addColorbar(self, **kwargs): + def addColorbar(self, axis=None, **kwargs): """Method to add a color bar to the axis Parameters ---------- + axis : None or object + the axis to add the colorbar + kwargs : str You can enter any Matplotlib colorbar parameter as a kwarg. See https://matplotlib.org/3.5.1/api/_as_gen/matplotlib.pyplot.colorbar.html """ - plt.colorbar(mappable=self.axis.get_images()[0], ax=self.axis, **kwargs) - def finishAxis(self, **kwargs): + curAx = axis + if curAx==None: + curAx = self.axis + + if 'vmin' in kwargs.keys(): + vmin = kwargs['vmin'] + kwargs.pop('vmin') + if 'vmax' in kwargs.keys(): + vmax = kwargs['vmax'] + kwargs.pop('vmax') + if 'vmin' in locals() and 'vmax' in locals(): + cbar = plt.colorbar(mappable=mpl.cm.ScalarMappable(norm=mpl.colors.Normalize(vmin=vmin, vmax=vmax), cmap=self.cmap), + ax=curAx, ticks=[vmin, vmin/2, 0, vmax/2, vmax], **kwargs) + cbar.set_label('Phase', rotation=270) + else: + plt.colorbar(mappable=self.axis.get_images()[0], ax=curAx, **kwargs) + + def addBackground(self, axis=None, **kwargs): + """Method to add striped gray background to populations - used in plotRaster with "colorbyPhase" + + Parameters + ---------- + axis : None or object + the axis to add the background + + kwargs : str + You can enter any Matplotlib colorbar parameter as a kwarg. See https://matplotlib.org/3.5.1/api/_as_gen/matplotlib.pyplot.colorbar.html + """ + from matplotlib.pyplot import yticks, barh + + curAx = axis + if curAx==None: + curAx = self.axis + + if 'popNumCells' in kwargs.keys(): + popSizes = kwargs['popNumCells'] + yTicksPos = [] + accum = 0 + for i, size in enumerate(popSizes): + yTicksPos.append(accum + popSizes[i] / 2) # yCenter of pop + accum += popSizes[i] + curAx.set_yticks(np.array(yTicksPos)) + curAx.set_yticklabels(kwargs['popLabels']) + curAx.set_xlim(kwargs['timeRange']) + curAx.set_ylim([0,accum]) + curAx.barh(yTicksPos, left=kwargs['timeRange'][0], width=kwargs['timeRange'][1]-kwargs['timeRange'][0]-1, + height=popSizes, align='center', color=['#E3E3E3', 'w'], alpha=0.5) + + def finishAxis(self, axis=None, **kwargs): """Method to finalize an axis Parameters @@ -819,7 +911,11 @@ def finishAxis(self, **kwargs): """ - self.formatAxis(**kwargs) + curAx = axis + if curAx==None: + curAx = self.axis + + self.formatAxis(axis=curAx, **kwargs) if 'saveData' in kwargs: if kwargs['saveData']: @@ -827,28 +923,33 @@ def finishAxis(self, **kwargs): if 'legend' in kwargs: if kwargs['legend'] is True: - self.addLegend(**kwargs) + self.addLegend(axis=curAx,**kwargs) elif type(kwargs['legend']) == dict: - self.addLegend(**kwargs['legend']) + self.addLegend(axis=curAx,**kwargs['legend']) if 'scalebar' in kwargs: if kwargs['scalebar'] is True: - self.addScalebar() + self.addScalebar(axis=curAx) elif type(kwargs['scalebar']) == dict: - self.addScalebar(**kwargs['scalebar']) + self.addScalebar(axis=curAx,**kwargs['scalebar']) if 'colorbar' in kwargs: if kwargs['colorbar'] is True: - self.addColorbar() + self.addColorbar(axis=curAx) elif type(kwargs['colorbar']) == dict: - self.addColorbar(**kwargs['colorbar']) + self.addColorbar(axis=curAx, **kwargs['colorbar']) if 'grid' in kwargs: - self.axis.minorticks_on() + curAx.minorticks_on() if kwargs['grid'] is True: - self.axis.grid() + curAx.grid() elif type(kwargs['grid']) == dict: - self.axis.grid(**kwargs['grid']) + curAx.grid(**kwargs['grid']) + + if 'background' in kwargs: + if type(kwargs['background']) == dict: + self.addBackground(axis=curAx, **kwargs['background']) + # If this is the only axis on the figure, finish the figure if (type(self.metafig.ax) != np.ndarray) and (type(self.metafig.ax) != list): @@ -866,6 +967,11 @@ def __init__(self, data, axis=None, **kwargs): super().__init__(data=data, axis=axis, **kwargs) self.kind = 'scatter' + if 'signal' in data: + self.kind = 'scatter&signal' + self.time = data.get('time') + self.signal = data.get('signal') + self.timeRange = data.get('timeRange') self.x = data.get('x') self.y = data.get('y') self.s = data.get('s') @@ -879,20 +985,46 @@ def __init__(self, data, axis=None, **kwargs): def plot(self, **kwargs): - scatterPlot = self.axis.scatter( - x=self.x, - y=self.y, - s=self.s, - c=self.c, - marker=self.marker, - linewidth=self.linewidth, - cmap=self.cmap, - norm=self.norm, - alpha=self.alpha, - linewidths=self.linewidths, - ) + if self.kind=='scatter': + scatterPlot = self.axis.scatter( + x=self.x, + y=self.y, + s=self.s, + c=self.c, + marker=self.marker, + linewidth=self.linewidth, + cmap=self.cmap, + norm=self.norm, + alpha=self.alpha, + linewidths=self.linewidths, + ) + self.finishAxis(**kwargs) + + elif self.kind=='scatter&signal': + scatterPlot = self.axis[0].scatter( + x=self.x, + y=self.y, + s=self.s, + c=self.c, + marker=self.marker, + linewidth=self.linewidth, + cmap=self.cmap, + norm=self.norm, + alpha=self.alpha, + linewidths=self.linewidths, + ) + + linePlot = self.axis[1].plot( + self.time, + self.signal, + ) + self.finishAxis(axis=self.axis[0],**kwargs) + self.axis[0].set_xlabel('') + self.axis[0].set_ylabel('Cells (grouped by populations)') + self.axis[1].set_xlabel('Time (ms)') + self.axis[1].set_ylabel('Filtered signal') + self.metafig.finishFig(**kwargs) - self.finishAxis(**kwargs) return self.fig diff --git a/netpyne/sim/setup.py b/netpyne/sim/setup.py index ab0d272c8..f5e864c50 100644 --- a/netpyne/sim/setup.py +++ b/netpyne/sim/setup.py @@ -453,7 +453,7 @@ def setupRecording(): # stim spike recording if 'plotRaster' in sim.cfg.analysis: if isinstance(sim.cfg.analysis['plotRaster'], dict) and 'include' in sim.cfg.analysis['plotRaster']: - netStimLabels = list(sim.net.params.stimSourceParams.keys()) + ['allNetStims'] + netStimLabels = list(sim.net.params.stimSourceParams.keys()) + ['allNetStims'] + ['all'] for item in sim.cfg.analysis['plotRaster']['include']: if item in netStimLabels: sim.cfg.recordStim = True diff --git a/netpyne/tutorials/voltage_movie_tut/voltage_movie_tut.py b/netpyne/tutorials/voltage_movie_tut/voltage_movie_tut.py index ae317bf45..dab55a4da 100644 --- a/netpyne/tutorials/voltage_movie_tut/voltage_movie_tut.py +++ b/netpyne/tutorials/voltage_movie_tut/voltage_movie_tut.py @@ -112,8 +112,10 @@ } ## Then we can replace `sim.runSim()` with: +def intervalFunc(t, **kwargs): + sim.analysis.plotShape(**kwargs) -sim.runSimWithIntervalFunc(1.0, sim.analysis.plotShape, timeRange=[10, 20], funcArgs=plotArgs) +sim.runSimWithIntervalFunc(1.0, intervalFunc, timeRange=[10, 20], funcArgs=plotArgs) ## This will execute `sim.analysis.plotShape` every 1.0 ms from 10 to 20 ms in the simulation and feed it the plotArgs dictionary we created above. From cdb1697294085256805fc9eeb2a14a08fb342d6d Mon Sep 17 00:00:00 2001 From: vvbragin Date: Wed, 25 Oct 2023 12:23:06 +0200 Subject: [PATCH 16/19] improvements in netParams validation (API and some error messages) --- netpyne/sim/setup.py | 5 +- netpyne/sim/validate.py | 169 --------------------------------------- netpyne/sim/validator.py | 158 ++++++++++++++++++++++-------------- 3 files changed, 99 insertions(+), 233 deletions(-) delete mode 100644 netpyne/sim/validate.py diff --git a/netpyne/sim/setup.py b/netpyne/sim/setup.py index f5e864c50..bedaa98ff 100644 --- a/netpyne/sim/setup.py +++ b/netpyne/sim/setup.py @@ -107,8 +107,9 @@ def initialize(netParams=None, simConfig=None, net=None): valid, failed = validator.validateNetParams(netParams) sim.timing('stop', 'validationTime') if failed: - failed = map(lambda entry: entry[0], failed) # get failed component name - print(f"\nNetParams validation identified some potential issues in {', '.join(failed)}. See above for details.") + failedComps = [err.component for err in failed] # get failed component name + failedComps = list(set(failedComps)) # keep unique elements only + print(f"\nNetParams validation identified some potential issues in {', '.join(failedComps)}. See above for details.") else: print("\nNetParams validation successful.") except Exception as e: diff --git a/netpyne/sim/validate.py b/netpyne/sim/validate.py deleted file mode 100644 index 45752a663..000000000 --- a/netpyne/sim/validate.py +++ /dev/null @@ -1,169 +0,0 @@ -from schema import Schema, Optional, And, Or, Use -from collections import ChainMap - -cell_spec = { - str: { - 'secs': { - Optional(And(str, Use(str.lower), lambda s: s in ['soma', 'dend'])): { - Optional('geom'): { - Optional('L'): Or(int, float), - Optional('Ra'): Or(int, float), - Optional('diam'): Or(int, float), - Optional('cm'): Or(int, float), - Optional('pt3d'): And( - [(float, float, float, float)], lambda t: len(list(filter(lambda x: len(x) != 4, t))) == 0 - ), - }, - Optional('mechs'): { - And(str, Use(str.lower), lambda s: s in ['hh', 'pas']): { - Optional('el'): int, - Optional('gkbar'): float, - Optional('gl'): float, - Optional('gnabar'): float, - Optional('g'): float, - Optional('e'): Or(int, float), - } - }, - Optional('topol'): { - Optional('parentSec'): And(str, Use(str.lower), lambda s: s in ['soma', 'dend']), - Optional('childX'): Or(int, float), - Optional('parentX'): Or(int, float), - }, - Optional('pointps'): { - str: { - 'mod': str, - 'C': Or(int, float), - 'k': Or(int, float), - 'vr': Or(int, float), - 'vt': Or(int, float), - 'vpeak': Or(int, float), - 'a': Or(int, float), - 'b': Or(int, float), - 'c': Or(int, float), - 'd': Or(int, float), - 'celltype': int, - } - }, - } - } - } -} - -population_spec = { - str: { - 'cellType': str, - 'numCells': int, - Optional('yRange'): [int], - Optional('ynormRange'): [float], - Optional('cellModel'): str, - } -} - - -synaptic_spec = { - str: { - 'mod': And(str, Use(str.lower), lambda s: s in ['exp2syn']), - 'tau1': Or(int, float), - 'tau2': Or(int, float), - 'e': Or(int, float), - } -} - - -stimulation_source_spec = { - str: { - Optional('type'): And(str, Use(str.lower), lambda s: s in ['iclamp', 'vclamp', 'alphasynapse', 'netstim']), - Optional('rate'): int, - Optional('noise'): float, - Optional('del'): int, - Optional('dur'): Or(int, [int]), - Optional('amp'): Or(str, [int]), - Optional('gain'): float, - Optional('tau1'): Or(int, float), - Optional('tau2'): Or(int, float), - Optional('rstim'): Or(int, float), - Optional('e'): Or(int, float), - Optional('gmax'): str, - Optional('onset'): str, - Optional('tau'): Or(int, float), - Optional('interval'): str, - Optional('start'): Or(int, float), - } -} - - -stimulation_target_spec = { - str: { - Optional('source'): str, - Optional('conds'): { - Optional('cellType'): Or(str, [str]), - Optional('cellList'): [Or(int, float)], - Optional('pop'): str, - Optional('ynorm'): [Or(int, float)], - }, - Optional('weight'): Or( - float, str - ), # The string is for capturing functions. May want to validate it's valid python - Optional('delay'): Or( - int, str - ), # The string is for capturing functions. May want to validate it's valid python - Optional('synMech'): str, - Optional('loc'): float, - Optional('sec'): str, - } -} - - -connection_spec = { - str: { - Optional('preConds'): { - Optional('pop'): Or(str, [str]), - Optional('y'): [Or(int, float)], - Optional('cellType'): str, - }, - Optional('postConds'): { - Optional('pop'): Or(str, [str]), - Optional('y'): [Or(int, float)], - Optional('cellType'): str, - }, - Optional(And(str, Use(str.lower), lambda s: s in ['probability', 'convergence', 'divergence'])): Or( - float, str - ), - Optional('weight'): Or( - float, str - ), # The string is for capturing functions. May want to validate it's valid python - Optional('delay'): Or( - int, str - ), # The string is for capturing functions. May want to validate it's valid python - Optional('synMech'): str, - Optional('loc'): float, - Optional('sec'): And(str, Use(str.lower), lambda s: s in ['dend']), - } -} - - -cell_schema = Schema(cell_spec) -population_schema = Schema(population_spec) -synaptic_schema = Schema(synaptic_spec) -stimulation_source_schema = Schema(stimulation_source_spec) -stimulation_target_schema = Schema(stimulation_target_spec) -connection_schema = Schema(connection_spec) - -net_param_schema = Schema( - dict( - ChainMap( - *[ - cell_spec, - population_spec, - synaptic_spec, - stimulation_source_spec, - stimulation_target_spec, - connection_spec, - ] - ) - ) -) - - -def check_netparams(net_params: dict): - cell_schema.validate(net_params.cellParam) diff --git a/netpyne/sim/validator.py b/netpyne/sim/validator.py index 1350a8109..07788e85e 100644 --- a/netpyne/sim/validator.py +++ b/netpyne/sim/validator.py @@ -17,6 +17,8 @@ def __init__(self, netParams): self.validateModels = True # cfg.validateNetParamsMechs +numberOrStringFunc = Or(int, float, str, error='Expected number (int, float) or a function as string.') + def general_specs(): specs = { '_labelid': int, @@ -64,6 +66,7 @@ def pop_specs(context): specs = { str: { + # TODO: either cellType or cellModel has to be present?? Optional('cellType'): And( str, lambda s: __isKeyIn(s, context.cellParams) or __isAmongConds(s, 'cellType', context.cellParams) @@ -92,7 +95,7 @@ def pop_specs(context): } ], Optional('numCells'): Or(int, float), - Optional('density'): Or(int, float, str), # string-based function is allowed + Optional('density'): numberOrStringFunc, # string-based function is allowed Optional('gridSpacing'): Or(And([Or(int, float)], lambda s: len(s) == 3), Or(int, float)), Optional('xRange'): And([Or(int, float)], lambda s: len(s) == 2), Optional('yRange'): And([Or(int, float)], lambda s: len(s) == 2), @@ -208,11 +211,11 @@ def cell_specs(context): Optional('secs'): { ## It is optional because it may NOT be a compartCell, but for compartCells this entry is mandatory str: { Optional('geom'): { - Optional('diam'): Or(int, float, str), - Optional('L'): Or(int, float, str), - Optional('Ra'): Or(int, float, str), - Optional('cm'): Or(int, float, str), - Optional('nseg'): Or(int, float, str), + Optional('diam'): numberOrStringFunc, + Optional('L'): numberOrStringFunc, + Optional('Ra'): numberOrStringFunc, + Optional('cm'): numberOrStringFunc, + Optional('nseg'): numberOrStringFunc, Optional('pt3d'): [ And( lambda s: len(s) == 4, # list of (list or tuples), each with 4 components @@ -233,14 +236,14 @@ def cell_specs(context): ), Optional('mechs'): { Optional('hh'): { # one possible built-in mechanism, very used - Optional('gnabar'): Or(int, float, str), - Optional('gkbar'): Or(int, float, str), - Optional('gl'): Or(int, float, str), - Optional('el'): Or(int, float, str), + Optional('gnabar'): numberOrStringFunc, + Optional('gkbar'): numberOrStringFunc, + Optional('gl'): numberOrStringFunc, + Optional('el'): numberOrStringFunc, }, Optional('pas'): { # another one - Optional('g'): Or(int, float, str), - Optional('e'): Or(int, float, str), + Optional('g'): numberOrStringFunc, + Optional('e'): numberOrStringFunc, }, Optional(str): { # other possibilities (nonlinear mechanisms: .mod) Optional( @@ -256,7 +259,7 @@ def cell_specs(context): # Optional('synMechs'): [{'label': str, 'loc': Or(int,float)}] Optional('pointps'): { str: { - 'mod': And( str, lambda s: __isPointpModel(s, context)), + 'mod': And( str, And(lambda s: __isPointpModel(s, context), error='no pointp'), error='Mod is bad'), # 'mod': And( str, And(lambda s: __isPointpModel(s, context), error='Bebe'), error='Meme'), Optional('loc'): Or(int, float), Optional('vref'): str, # voltage calculated in the .mod Optional('synList'): [ @@ -353,7 +356,7 @@ def cell_specs(context): # Other options are possible, for example those from IntFire1, etcetera. Optional(str): object, }, - Optional('vars'): {Optional(str): Or(int, float, str)}, + Optional('vars'): {Optional(str): numberOrStringFunc}, Optional(str): object, } } @@ -375,11 +378,11 @@ def synmech_specs(context): # lambda s: return True, # Options for ExpSyn - Optional('tau'): Or(int, float, str), - Optional('e'): Or(int, float, str), + Optional('tau'): numberOrStringFunc, + Optional('e'): numberOrStringFunc, # Options for Exp2Syn - Optional('tau1'): Or(int, float, str), - Optional('tau2'): Or(int, float, str), + Optional('tau1'): numberOrStringFunc, + Optional('tau2'): numberOrStringFunc, # Optional('e'): Or(int,float), # already set in ExpSyn Optional('pointerParams'): { 'target_var': str, @@ -442,9 +445,9 @@ def conn_specs(context): Optional(str): Or(Or(str, int, float), [Or(str, int, float)]), }, Optional('connFunc'): lambda s: s in ['fullConn', 'probConn', 'convConn', 'divConn', 'fromListConn'], - Optional('probability'): Or(int, float, str), # it can also be a string-based function - Optional('convergence'): Or(int, float, str), # it can also be a string-based function - Optional('divergence'): Or(int, float, str), # it can also be a string-based function + Optional('probability'): numberOrStringFunc, # it can also be a string-based function + Optional('convergence'): numberOrStringFunc, # it can also be a string-based function + Optional('divergence'): numberOrStringFunc, # it can also be a string-based function Optional('connList'): Or( [And (Or(tuple, list), lambda s: len(s) == 2 and all(isinstance(n, int) for n in s))], # list of 2-element lists/tuples of two ints (pre, post) lambda s: isinstance(s, np.ndarray) and s.shape[1] == 2 and s.dtype == 'int' # np.array of shape (x, 2) of ints @@ -795,7 +798,7 @@ def rxd_specs(): str: { 'reactant': str, # validity of the expression will not be checked 'product': str, # validity of the expression will not be checked - 'rate_f': Or(int, float, str), + 'rate_f': numberOrStringFunc, Optional('rate_b'): Or(int, float, str, None), Optional('regions'): Or(str, [str], [None]), Optional('custom_dynamics'): Or(bool, None) @@ -815,7 +818,7 @@ def rxd_specs(): str: { 'reactant': str, # validity of the expression will not be checked 'product': str, # validity of the expression will not be checked - 'rate_f': Or(int, float, str), + 'rate_f': numberOrStringFunc, Optional('rate_b'): Or(int, float, str, None), Optional('regions'): Or(str, [str], [None]), Optional('custom_dynamics'): Or(bool, None), @@ -827,7 +830,7 @@ def rxd_specs(): Optional('rates'): { str: { 'species': Or(str, [str]), # string-based specification (see rxd_net example) - 'rate': Or(int, float, str), + 'rate': numberOrStringFunc, Optional('regions'): Or(str, [str], [None]), Optional('membrane_flux'): bool, } @@ -844,19 +847,20 @@ def validateNetParams(net_params, printWarnings=True): global __mechVarList __mechVarList = None - def validate(data, specs, label): + def validate(data, specs, component): schema = Schema(specs) try: valid = schema.validate(data) - print(f" Successfully validated {label}") - validatedSchemas[label] = valid - except SchemaError as error: - msg = __parseErrorMessage(error.autos, origIndent=' ') + print(f" Successfully validated {component}") + validatedSchemas[component] = valid + except SchemaError as origError: + error = ValidationError(component, origError) + failedSchemas.append(error) + if printWarnings: - print(f"\n Error validating {label}:") - print(msg + "\n") - failedSchemas.append((label, error, msg)) + print(f"\n Error validating {component}:") + print(error.formattedMessage(baseIndent=' ') + "\n") context = ValidationContext(net_params) @@ -924,17 +928,12 @@ def validate(data, specs, label): ) if net_params.rxdParams: - validate( net_params.rxdParams, rxd_specs(), 'rxdParams' ) - if len(validatedSchemas) == 0: - validatedSchemas = None - if len(failedSchemas) == 0: - failedSchemas = None return validatedSchemas, failedSchemas # utils @@ -989,27 +988,54 @@ def isModel(name, modelType): return all(isModel(n, modelType) for n in name) return isModel(name, modelType) -def __parseErrorMessage(msg, origIndent=''): - import re - pattern = re.compile("key ('.*') error:", re.IGNORECASE) # TODO: need to freeze `schema` version to make sure this pattern is preserved - keySeq = [] - other = [] - # convert dict keys sequence to single string - for line in msg: - if line is None: line = '' - matches = pattern.match(line) - if matches: - matches = matches.groups() - if len(matches) > 0: - keySeq.append(matches[0]) # presume only one match - elif line != '': - other.append(line) - if len(keySeq) > 0: - newln = '\n' + origIndent + ' ' - other = newln + newln.join(other) - return f"{origIndent}Error in {' -> '.join(keySeq)}:{other}" - else: - return msg + +class ValidationError(object): + + def __init__(self, component, error): + self.component = component + self.originalError = error + + self.keyPath, self.summary = self.__parseErrorMessage() + + def formattedMessage(self, baseIndent=''): + + message = "" + if len(self.keyPath) > 0: + keySeq = ' -> '.join(self.keyPath) + message += f"{baseIndent}Error in {keySeq}:" + + newLine = '\n' + baseIndent + ' ' + if len(self.summary): + message += newLine + newLine.join(self.summary) + else: + message += newLine + '' + + return message + + def __parseErrorMessage(self, indent=''): + import re + pattern = re.compile("key ('.*') error:", re.IGNORECASE) # TODO: need to freeze `schema` version to make sure this pattern is preserved + keySeq = [] + other = [] + err = self.originalError + # convert dict keys sequence to single string + for line in err.autos: + if line is None: line = '' + matches = pattern.match(line) + if matches: + matches = matches.groups() + if len(matches) > 0: + keySeq.append(matches[0]) # presume only one match + elif line != '': + other.append(line) + + errors = [e for e in err.errors if e is not None] + if errors: # custom errors (provided by netpyne validator) + summary = [errors[-1]] # take the last error (and put into list for consistency) + else: # auto-generated by `schema` + summary = other + + return keySeq, summary @@ -1026,24 +1052,32 @@ def checkValidation(): def checkModelValid(index): print(f'PROCESSSING {index}') _, netParams = sim.loadModel(index, loadMechs=True, ignoreMechAlreadyExistsError=True) - _, failed = validator.validateNetParams(net_params=netParams) + valid, failed = validator.validateNetParams(net_params=netParams) if failed: print(f'FOUND {len(failed)} ERRORS IN {index}') - for (compName, error, errorAutos) in failed: + for compName in [f.component for f in failed]: print(compName) else: print(f'VALIDATION SUCCEEDED FOR {index}') + return valid, failed os.chdir('examples') try: + valid, failed = [], [] + # TODO: for some reason, the models below fail to load when run in order of for-loop below. # Works okay when run individually though... exceptionFromLoop = ['batchCellMapping/index.npjson', 'batchCell/index.npjson'] for index in [index for index in glob.glob('*/*.npjson') if index not in exceptionFromLoop ]: - checkModelValid(index) + v, f = checkModelValid(index) + valid.extend(v) + failed.extend(f) except Exception as e: print(f"FAILED VALIDATING EXAMPLES: {e}") - os.chdir('..') \ No newline at end of file + print(f'================\nValidation summary: {len(failed)} failed\n') + + os.chdir('..') + return failed \ No newline at end of file From 81def7a61f3b24c06ed8e215ba4bf9caa444ce30 Mon Sep 17 00:00:00 2001 From: vvbragin Date: Tue, 31 Oct 2023 21:56:29 +0100 Subject: [PATCH 17/19] fixed loading netParams in some scenarios (bug caused by srting functions pre-processing) --- CHANGES.md | 2 ++ netpyne/sim/setup.py | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index bdcc2a5ce..c9871d8b1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,8 @@ - Fix to automatically include netstims in the sim.allSimData object when plotRaster 'include' selects 'all' +- Fixed loading netParams in some scenarios (bug caused by srting functions pre-processing) + # Version 1.0.5 **New features** diff --git a/netpyne/sim/setup.py b/netpyne/sim/setup.py index bedaa98ff..ea7879f2f 100644 --- a/netpyne/sim/setup.py +++ b/netpyne/sim/setup.py @@ -94,8 +94,6 @@ def initialize(netParams=None, simConfig=None, net=None): sim.setNet(sim.Network()) # or create new network sim.setNetParams(netParams) # set network parameters - sim.net.params.synMechParams.preprocessStringFunctions() - sim.net.params.cellParams.preprocessStringFunctions() if sim.nhosts > 1: sim.cfg.validateNetParams = False # turn of error chceking if using multiple cores @@ -173,6 +171,9 @@ def setNetParams(params): # set mapping from netParams variables to cfg (used in batch) sim.net.params.setCfgMapping(sim.cfg) + sim.net.params.cellParams.preprocessStringFunctions() + sim.net.params.synMechParams.preprocessStringFunctions() + # ------------------------------------------------------------------------------ # Set simulation config From eb224a351c48c628caa1b528c1d4c36f98d7b0a0 Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Thu, 2 Nov 2023 11:12:20 +0000 Subject: [PATCH 18/19] fix(imports): only import matplotlib where needed and if gui is enabled Fixes #786 --- netpyne/analysis/csd.py | 3 - netpyne/plotting/plotCSD.py | 7 +- netpyne/plotting/plotLFPLocations.py | 2 - netpyne/plotting/plotLFPSpectrogram.py | 7 +- netpyne/plotting/plotRaster.py | 5 +- netpyne/plotting/plotSpikeFreq.py | 4 +- netpyne/plotting/plotSpikeHist.py | 5 +- netpyne/plotting/plotTimeSeries.py | 2 - netpyne/plotting/plotTimeSeriesPSD.py | 2 - netpyne/plotting/plotter.py | 125 +++++++++++++------------ netpyne/support/morlet.py | 5 +- netpyne/support/morphology.py | 4 +- netpyne/support/scalebar.py | 105 +++++++++++---------- netpyne/support/stackedBarGraph.py | 4 +- 14 files changed, 152 insertions(+), 128 deletions(-) diff --git a/netpyne/analysis/csd.py b/netpyne/analysis/csd.py index 73f06ae9b..ba61f7100 100644 --- a/netpyne/analysis/csd.py +++ b/netpyne/analysis/csd.py @@ -20,9 +20,6 @@ import numpy as np import scipy from numbers import Number -import matplotlib -from matplotlib import pyplot as plt -from matplotlib import ticker as ticker import json import sys import os diff --git a/netpyne/plotting/plotCSD.py b/netpyne/plotting/plotCSD.py index b2ddae562..fd9fbb5cf 100644 --- a/netpyne/plotting/plotCSD.py +++ b/netpyne/plotting/plotCSD.py @@ -1,10 +1,13 @@ # PLOTTING CSD +from netpyne import __gui__ +if __gui__: + import matplotlib + from matplotlib import pyplot as plt + from ..analysis.utils import exception, _showFigure import numpy as np import scipy -import matplotlib -from matplotlib import pyplot as plt @exception diff --git a/netpyne/plotting/plotLFPLocations.py b/netpyne/plotting/plotLFPLocations.py index 3c5e5c725..16511a5c4 100644 --- a/netpyne/plotting/plotLFPLocations.py +++ b/netpyne/plotting/plotLFPLocations.py @@ -1,7 +1,5 @@ # Generate plots of LFP (local field potentials) and related analyses -import matplotlib.patches as mpatches -import matplotlib.pyplot as plt import math from ..analysis.utils import exception # , loadData from ..analysis.tools import loadData diff --git a/netpyne/plotting/plotLFPSpectrogram.py b/netpyne/plotting/plotLFPSpectrogram.py index 33e3a91d4..b34558233 100644 --- a/netpyne/plotting/plotLFPSpectrogram.py +++ b/netpyne/plotting/plotLFPSpectrogram.py @@ -1,7 +1,10 @@ # Generate plots of LFP (local field potentials) and related analyses -import matplotlib.patches as mpatches -import matplotlib.pyplot as plt +from netpyne import __gui__ + +if __gui__: + import matplotlib.pyplot as plt + import math from ..analysis.utils import exception # , loadData from ..analysis.tools import loadData diff --git a/netpyne/plotting/plotRaster.py b/netpyne/plotting/plotRaster.py index a5be18c9a..0c3c44e84 100644 --- a/netpyne/plotting/plotRaster.py +++ b/netpyne/plotting/plotRaster.py @@ -1,6 +1,9 @@ # Generate a raster plot of spiking -import matplotlib.patches as mpatches +from netpyne import __gui__ + +if __gui__: + import matplotlib.patches as mpatches from ..analysis.utils import exception # , loadData from ..analysis.tools import loadData from .plotter import ScatterPlotter diff --git a/netpyne/plotting/plotSpikeFreq.py b/netpyne/plotting/plotSpikeFreq.py index fa639a71e..3c2ace377 100644 --- a/netpyne/plotting/plotSpikeFreq.py +++ b/netpyne/plotting/plotSpikeFreq.py @@ -1,7 +1,9 @@ # Generate a spike frequency plot +from netpyne import __gui__ +if __gui__: + import matplotlib.patches as mpatches import numpy as np -import matplotlib.patches as mpatches from numbers import Number from ..analysis.utils import exception, _smooth1d from ..analysis.tools import loadData diff --git a/netpyne/plotting/plotSpikeHist.py b/netpyne/plotting/plotSpikeHist.py index 7c7171f97..8a640083d 100644 --- a/netpyne/plotting/plotSpikeHist.py +++ b/netpyne/plotting/plotSpikeHist.py @@ -1,7 +1,10 @@ # Generate a spike histogram +from netpyne import __gui__ + +if __gui__: + import matplotlib.patches as mpatches import numpy as np -import matplotlib.patches as mpatches from ..analysis.utils import exception from ..analysis.tools import loadData from .plotter import HistPlotter diff --git a/netpyne/plotting/plotTimeSeries.py b/netpyne/plotting/plotTimeSeries.py index 2285ddd2d..9fcf78a7b 100644 --- a/netpyne/plotting/plotTimeSeries.py +++ b/netpyne/plotting/plotTimeSeries.py @@ -1,7 +1,5 @@ # Generate plots of LFP (local field potentials) and related analyses -import matplotlib.patches as mpatches -import matplotlib.pyplot as plt import math from ..analysis.utils import exception # , loadData from ..analysis.tools import loadData diff --git a/netpyne/plotting/plotTimeSeriesPSD.py b/netpyne/plotting/plotTimeSeriesPSD.py index dca158548..f659418ba 100644 --- a/netpyne/plotting/plotTimeSeriesPSD.py +++ b/netpyne/plotting/plotTimeSeriesPSD.py @@ -1,7 +1,5 @@ # Generate plots of LFP (local field potentials) and related analyses -import matplotlib.patches as mpatches -import matplotlib.pyplot as plt import math from ..analysis.utils import exception # , loadData from ..analysis.tools import loadData diff --git a/netpyne/plotting/plotter.py b/netpyne/plotting/plotter.py index be967d265..c8cc2851c 100644 --- a/netpyne/plotting/plotter.py +++ b/netpyne/plotting/plotter.py @@ -3,13 +3,17 @@ """ -import matplotlib as mpl -import matplotlib.pyplot as plt +from netpyne import __gui__ + +if __gui__: + import matplotlib as mpl + import matplotlib.pyplot as plt + from matplotlib.offsetbox import AnchoredOffsetbox + import numpy as np from copy import deepcopy import pickle, json import os -from matplotlib.offsetbox import AnchoredOffsetbox try: basestring @@ -1244,64 +1248,67 @@ def plot(self, **kwargs): return self.fig -class _AnchoredScaleBar(AnchoredOffsetbox): - """ - A class used for adding scale bars to plots - - Modified from here: https://gist.github.com/dmeliza/3251476 - """ - - def __init__( - self, - axis, - sizex=0, - sizey=0, - labelx=None, - labely=None, - loc=4, - pad=0.1, - borderpad=0.1, - sep=2, - prop=None, - barcolor="black", - barwidth=None, - **kwargs - ): +try: + class _AnchoredScaleBar(AnchoredOffsetbox): """ - Draw a horizontal and/or vertical bar with the size in data coordinate - of the give axes. A label will be drawn underneath (center-aligned). - - - transform : the coordinate frame (typically axes.transData) - - sizex,sizey : width of x,y bar, in data units. 0 to omit - - labelx,labely : labels for x,y bars; None to omit - - loc : position in containing axes - - pad, borderpad : padding, in fraction of the legend font size (or prop) - - sep : separation between labels and bars in points. - - **kwargs : additional arguments passed to base class constructor + A class used for adding scale bars to plots + + Modified from here: https://gist.github.com/dmeliza/3251476 """ - from matplotlib.patches import Rectangle - from matplotlib.offsetbox import AuxTransformBox, VPacker, HPacker, TextArea, DrawingArea - - bars = AuxTransformBox(axis.transData) - if sizex: - if axis.xaxis_inverted(): - sizex = -sizex - bars.add_artist(Rectangle((0, 0), sizex, 0, ec=barcolor, lw=barwidth, fc="none")) - if sizey: - if axis.yaxis_inverted(): - sizey = -sizey - bars.add_artist(Rectangle((0, 0), 0, sizey, ec=barcolor, lw=barwidth, fc="none")) - - if sizex and labelx: - self.xlabel = TextArea(labelx) - bars = VPacker(children=[bars, self.xlabel], align="center", pad=0, sep=sep) - if sizey and labely: - self.ylabel = TextArea(labely) - bars = HPacker(children=[self.ylabel, bars], align="center", pad=0, sep=sep) - - AnchoredOffsetbox.__init__( - self, loc, pad=pad, borderpad=borderpad, child=bars, prop=prop, frameon=False, **kwargs - ) + + def __init__( + self, + axis, + sizex=0, + sizey=0, + labelx=None, + labely=None, + loc=4, + pad=0.1, + borderpad=0.1, + sep=2, + prop=None, + barcolor="black", + barwidth=None, + **kwargs + ): + """ + Draw a horizontal and/or vertical bar with the size in data coordinate + of the give axes. A label will be drawn underneath (center-aligned). + + - transform : the coordinate frame (typically axes.transData) + - sizex,sizey : width of x,y bar, in data units. 0 to omit + - labelx,labely : labels for x,y bars; None to omit + - loc : position in containing axes + - pad, borderpad : padding, in fraction of the legend font size (or prop) + - sep : separation between labels and bars in points. + - **kwargs : additional arguments passed to base class constructor + """ + from matplotlib.patches import Rectangle + from matplotlib.offsetbox import AuxTransformBox, VPacker, HPacker, TextArea, DrawingArea + + bars = AuxTransformBox(axis.transData) + if sizex: + if axis.xaxis_inverted(): + sizex = -sizex + bars.add_artist(Rectangle((0, 0), sizex, 0, ec=barcolor, lw=barwidth, fc="none")) + if sizey: + if axis.yaxis_inverted(): + sizey = -sizey + bars.add_artist(Rectangle((0, 0), 0, sizey, ec=barcolor, lw=barwidth, fc="none")) + + if sizex and labelx: + self.xlabel = TextArea(labelx) + bars = VPacker(children=[bars, self.xlabel], align="center", pad=0, sep=sep) + if sizey and labely: + self.ylabel = TextArea(labely) + bars = HPacker(children=[self.ylabel, bars], align="center", pad=0, sep=sep) + + AnchoredOffsetbox.__init__( + self, loc, pad=pad, borderpad=borderpad, child=bars, prop=prop, frameon=False, **kwargs + ) +except NameError: + print("-nogui passed, matplotlib is unavailable") def _add_scalebar( diff --git a/netpyne/support/morlet.py b/netpyne/support/morlet.py index ca2df6bff..a58b1b793 100644 --- a/netpyne/support/morlet.py +++ b/netpyne/support/morlet.py @@ -15,9 +15,12 @@ from __future__ import unicode_literals from __future__ import absolute_import +from netpyne import __gui__ +if __gui__: + import matplotlib.pyplot as plt + import numpy as np import scipy.signal as sps -import matplotlib.pyplot as plt def index2ms(idx, sampr): diff --git a/netpyne/support/morphology.py b/netpyne/support/morphology.py index e17d26352..4b2025625 100644 --- a/netpyne/support/morphology.py +++ b/netpyne/support/morphology.py @@ -20,7 +20,9 @@ from builtins import object import numpy as np import pylab as plt -from matplotlib.pyplot import cm +from netpyne import __gui__ +if __gui__: + from matplotlib.pyplot import cm import string from neuron import h import numbers diff --git a/netpyne/support/scalebar.py b/netpyne/support/scalebar.py index e6ec7b375..786835f04 100644 --- a/netpyne/support/scalebar.py +++ b/netpyne/support/scalebar.py @@ -16,56 +16,61 @@ from future import standard_library standard_library.install_aliases() -from matplotlib.offsetbox import AnchoredOffsetbox - - -class AnchoredScaleBar(AnchoredOffsetbox): - def __init__( - self, - transform, - sizex=0, - sizey=0, - labelx=None, - labely=None, - loc=4, - pad=0.1, - borderpad=0.1, - sep=2, - prop=None, - barcolor="black", - barwidth=None, - **kwargs - ): - """ - Draw a horizontal and/or vertical bar with the size in data coordinate - of the give axes. A label will be drawn underneath (center-aligned). - - transform : the coordinate frame (typically axes.transData) - - sizex,sizey : width of x,y bar, in data units. 0 to omit - - labelx,labely : labels for x,y bars; None to omit - - loc : position in containing axes - - pad, borderpad : padding, in fraction of the legend font size (or prop) - - sep : separation between labels and bars in points. - - **kwargs : additional arguments passed to base class constructor - """ - from matplotlib.patches import Rectangle - from matplotlib.offsetbox import AuxTransformBox, VPacker, HPacker, TextArea, DrawingArea - - bars = AuxTransformBox(transform) - if sizex: - bars.add_artist(Rectangle((0, 0), sizex, 0, ec=barcolor, lw=barwidth, fc="none")) - if sizey: - bars.add_artist(Rectangle((0, 0), 0, sizey, ec=barcolor, lw=barwidth, fc="none")) - - if sizex and labelx: - self.xlabel = TextArea(labelx, minimumdescent=False) - bars = VPacker(children=[bars, self.xlabel], align="center", pad=0, sep=sep) - if sizey and labely: - self.ylabel = TextArea(labely) - bars = HPacker(children=[self.ylabel, bars], align="center", pad=0, sep=sep) - - AnchoredOffsetbox.__init__( - self, loc, pad=pad, borderpad=borderpad, child=bars, prop=prop, frameon=False, **kwargs - ) +from netpyne import __gui__ + +if __gui__: + from matplotlib.offsetbox import AnchoredOffsetbox + +try: + class AnchoredScaleBar(AnchoredOffsetbox): + def __init__( + self, + transform, + sizex=0, + sizey=0, + labelx=None, + labely=None, + loc=4, + pad=0.1, + borderpad=0.1, + sep=2, + prop=None, + barcolor="black", + barwidth=None, + **kwargs + ): + """ + Draw a horizontal and/or vertical bar with the size in data coordinate + of the give axes. A label will be drawn underneath (center-aligned). + - transform : the coordinate frame (typically axes.transData) + - sizex,sizey : width of x,y bar, in data units. 0 to omit + - labelx,labely : labels for x,y bars; None to omit + - loc : position in containing axes + - pad, borderpad : padding, in fraction of the legend font size (or prop) + - sep : separation between labels and bars in points. + - **kwargs : additional arguments passed to base class constructor + """ + from matplotlib.patches import Rectangle + from matplotlib.offsetbox import AuxTransformBox, VPacker, HPacker, TextArea, DrawingArea + + bars = AuxTransformBox(transform) + if sizex: + bars.add_artist(Rectangle((0, 0), sizex, 0, ec=barcolor, lw=barwidth, fc="none")) + if sizey: + bars.add_artist(Rectangle((0, 0), 0, sizey, ec=barcolor, lw=barwidth, fc="none")) + + if sizex and labelx: + self.xlabel = TextArea(labelx, minimumdescent=False) + bars = VPacker(children=[bars, self.xlabel], align="center", pad=0, sep=sep) + if sizey and labely: + self.ylabel = TextArea(labely) + bars = HPacker(children=[self.ylabel, bars], align="center", pad=0, sep=sep) + + AnchoredOffsetbox.__init__( + self, loc, pad=pad, borderpad=borderpad, child=bars, prop=prop, frameon=False, **kwargs + ) +except NameError: + print("-nogui passed, matplotlib is unavailable") def add_scalebar( diff --git a/netpyne/support/stackedBarGraph.py b/netpyne/support/stackedBarGraph.py index 3f7691a13..176c85bf2 100644 --- a/netpyne/support/stackedBarGraph.py +++ b/netpyne/support/stackedBarGraph.py @@ -48,7 +48,9 @@ ############################################################################### import numpy as np -from matplotlib import pyplot as plt +from netpyne import __gui__ +if __gui__: + from matplotlib import pyplot as plt ############################################################################### From 47c9321ce2aa5e3aeb1e6f43a56817f26df2ab50 Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Thu, 2 Nov 2023 13:36:54 +0000 Subject: [PATCH 19/19] feat(deps): add min pyneuroml release This is required to prevent matplotlib from being imported on HPCs. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 07c0454e9..da53720c0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ pandas future tables bokeh -pyneuroml +pyneuroml>=1.1.5 neuron pytest inspyred