diff --git a/core/hoc/ConfigParser.hoc b/core/hoc/ConfigParser.hoc deleted file mode 100644 index de276eb6..00000000 --- a/core/hoc/ConfigParser.hoc +++ /dev/null @@ -1,293 +0,0 @@ -/** - * @file ConfigParser.hoc - * @brief The ConfigParser will handle reading BlueConfig files and make the information available - * @brief to other modules. Whether the data read from the file should be stored in objects defined - * @brief in other files may be revised later. For now, store everything in generic "map" class built - * @brief from basic implementation - * @author king - * @date 2009-06-24 - * @remark Copyright © BBP/EPFL 2005-2011; All rights reserved. Do not distribute without further notice. - */ - -{load_file("nrngui.hoc")} -{load_file("Map.hoc")} -{load_file("fileUtils.hoc")} - -//------------------------------------------------------------------------------------------------------------ -//------------------------------------------------------------------------------------------------------------ -//------------------------------------------------------------------------------------------------------------ - -/*! - * For a simple quick version, just have "map" objects that link up to reports, stims, etc. These in turn - * contain maps for the actual contents of a given block - */ -begintemplate ConfigParser - //TODO: make a better encapsulated version - -external terminate - -//------------------------------------------------------------------------------------------------------------ -// Member fields -//------------------------------------------------------------------------------------------------------------ - -objref parsedRun, parsedReports, parsedStimuli, parsedInjects, parsedConnects, parsedElectrodes, \ - parsedConfigures, parsedModifications, parsedProjections, strUtil - -//------------------------------------------------------------------------------------------------------------ -// Public accessible -//------------------------------------------------------------------------------------------------------------ - -public init, open, toggleVerbose -public parsedRun, parsedReports, parsedStimuli, parsedInjects, parsedConnects, parsedElectrodes, \ - parsedConfigures, parsedModifications, parsedProjections - -//------------------------------------------------------------------------------------------------------------ -// Member function implementations -//------------------------------------------------------------------------------------------------------------ - -proc init() { - strUtil = new StringFunctions() - - // note that since there should only be one Run object, we create nothing now. - parsedReports = new Map() - parsedStimuli = new Map() - parsedInjects = new Map() - parsedConnects = new Map() - parsedElectrodes = new Map() - parsedConfigures = new Map() - parsedModifications = new Map() - parsedProjections = new Map() -} - -//------------------------------------------------------------------------------------------------------------ - -/*! - * Read the specified Config File, populating basic maps with the data read for now. - * - * @param $s1 Name of file to open and parse - * @param $2 verbose - */ -proc open() { local commentCheck, verbose localobj configIn, sectionName, sectionType, tempBlock - verbose = 0 - if( numarg() >= 2 ) { - verbose = $2 - } - - configIn = new File() - configIn.ropen( $s1 ) - - if( configIn.isopen == 0 ) { - terminate( "Error: Could not open BlueConfig file", $s1 ) - } - - //read line by line, branch off when encountering major block type: run, stimulus, report, stimulusinject, connect - strdef rawline - strdef strtemp - sectionType = new String() - sectionName = new String() - - while( configIn.gets(rawline) >= 0 ) { - trim( rawline ) - - if( isEmpty(rawline) ) { - continue - } - if( isCommented(rawline) ) { - // This can skip whole section. It is a section if next line starts with { - sectionType.s = rawline - configIn.gets(rawline) - // { may be commented - strUtil.tail(rawline, "[ \t#]*", strtemp) - strUtil.left(strtemp, 1) - - if( strcmp(strtemp, "{") == 0 ) { - if( verbose ) { print " [SKIP] Section: ", sectionType.s } - while( ! isTerminated(rawline) && configIn.gets(rawline) >= 0 ) { - strUtil.tail(rawline, "[ \t#]*", rawline) - } - continue - } else if ( isCommented(rawline) ) { - // If it's not a section and is commented, skip, otherwise parse normally - continue - } - } - - //non-comment line, there should be a name along with the yet to be determined section type - sectionType.s = "" - sscanf( rawline, "%s%s", sectionType.s, sectionName.s ) - - //next line is open brace, so grab it now - configIn.gets(strtemp) - if ( verbose ) { print " ConfigParser: Parsing section ", sectionType.s, ": ", sectionName.s } - - if( strcmp( sectionType.s, "Run" ) == 0 ) { - if( object_id(parsedRun) > 0 ) { - //user is only allowed to declare one Run section in the config file - terminate("Error: multiple Run objects declared in config file") - } - parsedRun = parseBlock(configIn) - } else { - tempBlock = parseBlock( configIn ) - if ( strcmp( sectionType.s, "Report" ) == 0 ) { - parsedReports.put( sectionName, tempBlock ) - } else if ( strcmp( sectionType.s, "Stimulus" ) == 0 ) { - parsedStimuli.put( sectionName, tempBlock ) - } else if ( strcmp( sectionType.s, "StimulusInject" ) == 0 ) { - parsedInjects.put( sectionName, tempBlock ) - } else if ( strcmp( sectionType.s, "Connection" ) == 0 ) { - parsedConnects.put( sectionName, tempBlock ) - } else if ( strcmp( sectionType.s, "Electrode" ) == 0 ){ - parsedElectrodes.put( sectionName, tempBlock ) - } else if ( strcmp( sectionType.s, "NeuronConfigure" ) == 0 ){ - parsedConfigures.put( sectionName, tempBlock ) - } else if ( strcmp( sectionType.s, "Modification" ) == 0 ) { - parsedModifications.put( sectionName, tempBlock ) - } else if ( strcmp( sectionType.s, "Projection" ) == 0 ) { - parsedProjections.put( sectionName, tempBlock ) - } - } - } - - configIn.close -} - - -//------------------------------------------------------------------------------------------------------------ - -/*! - * Generic method for parsing the field/value pairs bwteen the opening/closing braces in the config file - * - * @param $o1 File getting parsed - */ -obfunc parseBlock() { localobj pendingBlock, configIn, fieldName, fieldValue - strdef rawline - configIn = $o1 - pendingBlock = new Map() - - while( configIn.gets(rawline) >= 0 ) { - trim( rawline ) - if( isEmpty(rawline) || isCommented(rawline) ) { - continue - } - if( isTerminated(rawline) ) { - return pendingBlock - } - - //just grab the first string, then everything after that string - fieldName = new String() - fieldValue = new String() - sscanf( rawline, "%s", fieldName.s ) - strUtil.tail( rawline, fieldName.s, fieldValue.s ) - - // Trim eventual leading white chars (trailing ones removed before split) - trimFront( fieldValue.s ) - - if( pendingBlock.exists(fieldName.s) ) { - terminate("Key defined twice in Config", fieldName.s) - } - - //add to the map - pendingBlock.put( fieldName, fieldValue ) - } -} - -//------------------------------------------------------------------------------------------------------------ - -/** - * Remove white space from start and end of a string - * @param $s1 string to get trimmed - */ -proc trimFront() { - strUtil.tail($s1, "[ \t]*", $s1) -} - - -proc trimBack() { local idx - strdef lastchar - - while( strUtil.len($s1) ) { - lastchar=$s1 - idx=strUtil.len($s1) - 1 - strUtil.right(lastchar, idx) - if( strUtil.head(lastchar, "[ \t\n]", lastchar) > -1 ) { - strUtil.left($s1, idx) - } else { - break - } - } -} - - -proc trim() { - trimFront($s1) - trimBack($s1) -} - - -//------------------------------------------------------------------------------------------------------------ - -/*! - * @param $s1 Line to check for if it is commented or not - * @return 1 if the line is commented (first non-ws character is a '#') or 0 if not commented - */ -func isCommented() { - strdef temp - sscanf( $s1, "%s", temp ) - - //verify first non-ws charater is not '#'; otherwise, ignore whole line as comment - if( strUtil.substr( temp, "#" ) != 0 ) { - return 0 - } else { - return 1 - } -} - -//------------------------------------------------------------------------------------------------------------ - -/*! - * @param $s1 Line to check for if terminating close brace is present - * @return 1 if the line contains closing brace '}' to terminate section - */ -func isTerminated() { - strdef temp - sscanf( $s1, "%s", temp ) - - if( strcmp( temp, "}" ) != 0 ) { - return 0 - } else { - return 1 - } -} - -func isEmpty() { - return ( strcmp($s1, "") == 0 || strcmp($s1, "\n") == 0 ) -} - -//------------------------------------------------------------------------------------------------------------ - -/*! - * For all the maps stored in the ConfigParser, activate verbose logging so that any unexpected events are - * more descriptively printed to the console - */ -proc toggleVerbose() { local mapIndex, itemIndex localobj mapList - parsedRun.toggleVerbose() - - mapList = new List() - mapList.append( parsedReports ) - mapList.append( parsedStimuli ) - mapList.append( parsedInjects ) - mapList.append( parsedConnects ) - mapList.append( parsedElectrodes ) - mapList.append( parsedConfigures ) - mapList.append( parsedModifications ) - mapList.append( parsedProjections ) - - for mapIndex=0, mapList.count()-1 { - mapList.o(mapIndex).toggleVerbose() - for itemIndex=0, mapList.o(mapIndex).count()-1 { - mapList.o(mapIndex).o(itemIndex).toggleVerbose() - } - } -} - -endtemplate ConfigParser diff --git a/core/hoc/ElectrodeManager.hoc b/core/hoc/ElectrodeManager.hoc deleted file mode 100644 index d3d9ff07..00000000 --- a/core/hoc/ElectrodeManager.hoc +++ /dev/null @@ -1,138 +0,0 @@ -/** - * @file ElectrodeManager.hoc - * @brief - * @author reimann - * @date 2010-12-30 - * @remark Copyright © BBP/EPFL 2005-2011; All rights reserved. Do not distribute without further notice. - */ - -begintemplate ElectrodeManager - -objref xPosVec, yPosVec, zPosVec, electrodeName, electrodeList, electrodePath - -public getElectrode, init, clear, count, getName - -/*! - * Initiate the manager. - * Input: $o1: a String object giving the path to the Electrodes directory - * $o2: the map of all parsed Electrode blocks - */ -proc init() { local i, vNum, pX, pY, pZ localobj parsedElectrodes, pathString, tempElectrode, nil //, currName, currMap - xPosVec = new Vector() - yPosVec = new Vector() - zPosVec = new Vector() - electrodeName = new List() - electrodeList = new List() - - electrodePath = $o1 - parsedElectrodes = $o2 - - if(electrodePath == nil || parsedElectrodes == nil){ - //print "No ElectrodesPath or no Electrodes specified. Extracellular class of stimuli will be unavailable!" - return - } - - for( i = 0; i < parsedElectrodes.count(); i = i+1){ - //GET FILENAME - pathString = new String() - //currName = parsedElectrodes.key(i) - //currMap = parsedElectrodes.o(i) - if(parsedElectrodes.o(i).get("File") == nil){ - print "No filename given for Electrode ", parsedElectrodes.key(i).s, "cannot parse anything!" - continue - } - sprint(pathString.s,"%s/%s",electrodePath.s,parsedElectrodes.o(i).get("File").s) - - //GET LOCATION - pX = 0 - pY = 0 - pZ = 0 - if(parsedElectrodes.o(i).get("x") != nil){ - sscanf(parsedElectrodes.o(i).get("x").s,"%i",&pX) - } - if(parsedElectrodes.o(i).get("y") != nil){ - sscanf(parsedElectrodes.o(i).get("y").s,"%i",&pY) - } - if(parsedElectrodes.o(i).get("z") != nil){ - sscanf(parsedElectrodes.o(i).get("z").s,"%i",&pZ) - } - - //GET VERSION AND FINALLY THE OBJECT - vNum = -1 - if(parsedElectrodes.o(i).get("Version") != nil){ - sscanf(parsedElectrodes.o(i).get("Version").s,"%i",&vNum) - } - if(vNum==5){ - tempElectrode = new lookupTableV2(pathString.s) - if(vNum != tempElectrode.vInfo()){ - print "LookupTable version numbers dont match: ", pathString.s - continue - } - }else if(vNum<5){ - print "LookupTable version number < 5 no longer supported: ", pathString.s - continue - }else { - print "LookupTable unknown version number encountered: ", pathString.s - continue - } - - //ADD INFORMATION TO THE LISTS - xPosVec.append(pX) - yPosVec.append(pY) - zPosVec.append(pZ) - electrodeName.append(parsedElectrodes.key(i)) - electrodeList.append(tempElectrode) - } -} - -/*! - * Get an Electrode object from the manager. Lookup either by name: - * $s1: Name of the Electrode object (as specified in its Electrode { block) - * Or by position: - * $1: x-coord of the Electrode object - * $2: y-coord of the Electrode object - * $3: z-coord of the Electrode object - * Or by index: - * $1: Index of the Electrode object - * Returns the first Electrode object found within 5 micron (in each direction) of the specified coordinates - */ - -obfunc getElectrode(){ local i localobj tElec - if(numarg() == 1){ - if(argtype(1) == 2){ - for(i = 0; i < electrodeName.count(); i=i+1){ - if(strcmp($s1,electrodeName.o(i).s)==0){ - return electrodeList.o(i) - } - } - } else { - return electrodeList.o($1) - } - } else if(numarg() == 3){ //TODO: Need a better concept of how to handle the lookup by location. For now something simple - for(i = 0; i < electrodeName.count(); i=i+1){ - if((abs(xPosVec.x(i)-$1)<5) && (abs(yPosVec.x(i)-$2)<5) && (abs(zPosVec.x(i)-$3)<5)){ - return electrodeList.o(i) - } - } - } - return tElec -} - -obfunc getName(){ - return electrodeName.o($1) -} - -func count(){ - return electrodeList.count() -} - -proc clear(){ localobj nil - xPosVec.resize(0) - yPosVec.resize(0) - zPosVec.resize(0) - electrodeList.remove_all() - electrodeName.remove_all() -} - - -endtemplate ElectrodeManager diff --git a/core/hoc/RNGSettings.hoc b/core/hoc/RNGSettings.hoc index a89f1313..51819462 100644 --- a/core/hoc/RNGSettings.hoc +++ b/core/hoc/RNGSettings.hoc @@ -5,7 +5,6 @@ * @date 2017-03-08 * @remark Copyright © BBP/EPFL 2005-2017; All rights reserved. Do not distribute without further notice. */ -{load_file("SimSettings.hoc")} {load_file("fileUtils.hoc")} rngMode = 0 // corresponds to COMPATIBILITY defined below @@ -20,7 +19,7 @@ begintemplate RNGSettings public init, interpret, getRNGMode, getIonChannelSeed, getSynapseSeed, getStimulusSeed, getMinisSeed, getGlobalSeed public COMPATIBILITY, RANDOM123, UPMCELLRAN4 -external rngMode, ionchannelSeed, synapseSeed, stimulusSeed, minisSeed, globalSeed, simConfig, terminate +external rngMode, ionchannelSeed, synapseSeed, stimulusSeed, minisSeed, globalSeed, terminate proc init() { // consts for random number handling @@ -33,11 +32,16 @@ proc init() { * Given the Run block from a BlueConfig, check if there are special flags governing rng behavior * * $o1 ParsedRun from a BlueConfig (type is Map) + * $2 coreNeuronUsed: When simulating with CoreNeuron (default: false) */ -proc interpret() { localobj runInfo, pc, rng +proc interpret() { local coreNeuronUsed localobj runInfo, pc, rng strdef rngModeField runInfo = $o1 rngMode = RANDOM123 + coreNeuronUsed = $2 + if (numarg() < 2) { + coreNeuronUsed = 0 + } pc = new ParallelContext() if( pc.id() == 0 ) { @@ -60,7 +64,7 @@ proc interpret() { localobj runInfo, pc, rng } } - if( simConfig.coreNeuronUsed() && rngMode != RANDOM123) { + if( coreNeuronUsed && rngMode != RANDOM123) { // Given R123 is now the default there's no sense to allow an explicit invalid option terminate( "Config Error: CoreNEURON requires Random123 RNG" ) } diff --git a/core/hoc/SimSettings.hoc b/core/hoc/SimSettings.hoc deleted file mode 100644 index d5eb7d00..00000000 --- a/core/hoc/SimSettings.hoc +++ /dev/null @@ -1,177 +0,0 @@ -/** - * @file SimSettings.hoc - * @brief To help co-ordinating simulations with NEURON and CoreNEURON. - * @remark Copyright © BBP/EPFL 2005-2017; All rights reserved. Do not distribute without further notice. - */ - -{load_file("defvar.hoc")} -{load_file("fileUtils.hoc")} - -simulatorMode = 0 // corresponds to NEURON -objref coreneuronDataDir, coreneuronOutputDir - - -begintemplate SimSettings - -public init, interpret, execResult -public getSimulatorMode, generateData, runNeuron, getCoreneuronDataDir, coreNeuronUsed, getCoreneuronOutputDir, getMorphologyPath, getMorphologyExtension -public NEURON, CORENEURON, NEURON_CORENEURON - -external simulatorMode, coreneuronDataDir, simulator, coreneuronOutputDir, ospath, terminate, _py -objref this -objref morphologyPath, morphologyExtension - -/* BlueConfig can specify simulator mode : NEURON, CORENEURON or NEURON_CORENEURON - * - * NEURON : Run simulation using NEURON (current default behaviour) - * CORENEURON : Build model with NEURON, dump dataset to disk and continue simulation with CoreNEURON - * NEURON_CORENEURON : Build model with NEURON, dump dataset to disk, run simulation with NEURON - * and also run same simulation with CoreNEURON. This is more for debugging purpose and - * comparing results between two simulators. - */ - -// constants for simulation mode handling -proc init() { - morphologyPath = new String() - morphologyExtension = new String() - NEURON = 0 - CORENEURON = 1 - NEURON_CORENEURON = 2 -} - -/** - * Given the Run block from a BlueConfig, check simulator to be used - * $o1 ParsedRun from a BlueConfig (type is Map) - */ -proc interpret() { localobj runInfo, pc, rng, commandString, configMorphPath - runInfo = $o1 - pc = new ParallelContext() - - if( runInfo.exists( "MorphologyPath" ) ) { - configMorphPath = runInfo.get( "MorphologyPath" ) - if(runInfo.exists("MorphologyType")) { - morphologyExtension.s = runInfo.get( "MorphologyType" ).s - morphologyPath.s = configMorphPath.s - if( pc.id == 0 ) { print "MorphologyType specified in BlueConfig: ", morphologyExtension.s, ". Using full MorphologyPath: ", morphologyPath.s } - } else { - sprint(morphologyPath.s, "%s/ascii", configMorphPath.s) - morphologyExtension.s = "asc" - if( pc.id == 0 ) { print "No MorphologyType in BlueConfig, default to legacy 'asc'. MorphologyPath becomes: ", morphologyPath.s } - } - } else { - terminate( "MorphologyPath not available in the BlueConfig. Terminating" ) - } - - if( runInfo.exists( "Simulator" ) ) { - if( pc.id == 0 ) print "Taking Simulator mode from BlueConfig" - simulator = runInfo.get( "Simulator" ).s - } - - // check if simulator option is set in BlueConfig, abort if invalid - if( strcmp( simulator, "NEURON" ) == 0 ) { - simulatorMode = NEURON - } else if( strcmp( simulator, "CORENEURON" ) == 0 ) { - simulatorMode = CORENEURON - } else if( strcmp( simulator, "NEURON_CORENEURON" ) == 0 ) { - simulatorMode = NEURON_CORENEURON - } else { - strdef errmsg - sprint( errmsg, "Invalid SimulatorMode '%s'; use NEURON, CORENEURON or NEURON_CORENEURON. Terminating", simulator) - terminate( errmsg ) - } - - // create directory for dumping dataset into separate directory inside OutputRoot - if( simulatorMode != NEURON ) { - // create directory for dumping coreneuron binary dataset - coreneuronDataDir = new String() - coreneuronOutputDir = new String() - sprint(coreneuronDataDir.s, "%s/coreneuron_input", runInfo.get( "OutputRoot" ).s ) - sprint(coreneuronOutputDir.s, "%s/", runInfo.get( "OutputRoot" ).s ) - if( pc.id() == 0 ) { - commandString = new String() - execResult = _py.check_output_directory(coreneuronOutputDir.s) - if( execResult < 0 ) { - strdef errmsg - sprint( errmsg, "Error while creating directory '%s'. Terminating.", coreneuronOutputDir.s) - terminate( errmsg ) - } - - // if we are Restoring, then we will reuse the datadirectory - if( runInfo.exists( "Restore" ) ) { - nrnpython( "import os" ) - sprint( commandString.s, "os.symlink( os.path.realpath(\"%s/../coreneuron_input\"), \"%s\" )", runInfo.get( "Restore" ).s, coreneuronDataDir.s ) - print commandString.s - nrnpython( commandString.s ) - } else { - execResult = _py.check_output_directory(coreneuronDataDir.s) - if( execResult < 0 ) { - strdef errmsg - sprint( errmsg, "Error while creating directory '%s'. Terminating.", coreneuronOutputDir.s) - terminate( errmsg ) - } - } - } - } - - if( pc.id() == 0 ) { - print "SimulatorMode set to '", simulator, "'" - } - - if( runInfo.exists( "RandomizeGabaRiseTime" ) ) { - execute("{load_file(\"GABAABHelper.hoc\")}") - if( name_declared("randomize_Gaba_risetime") ) { - // Set the HOC global variable randomize_Gaba_risetime - strdef cmd - sprint( cmd, "randomize_Gaba_risetime=\"%s\"", runInfo.get( "RandomizeGabaRiseTime" ).s ) - execute( cmd ) - } else { - terminate( "Models don't support the parameter RandomizeGabaRiseTime, please load a newer version." ) - } - } - - pc.barrier() -} - -func getSimulatorMode() { - return simulatorMode -} - -// NEURON simulator mode doesn't need data generation -func generateData() { - return (simulatorMode != NEURON) -} - -// true if coreneuron is specified -func coreNeuronUsed() { - return (simulatorMode != NEURON) -} - -// Don't run NEURON if "exclusive" CoreNEURON mode specified -func runNeuron() { - return (simulatorMode != CORENEURON) -} - -// Directory where data will be dumped for CoreNEURON -obfunc getCoreneuronDataDir() { - return coreneuronDataDir -} - -// Directory where coreneuron will be output files -obfunc getCoreneuronOutputDir() { - return coreneuronOutputDir -} - -obfunc getMorphologyPath() { - return morphologyPath -} - -obfunc getMorphologyExtension() { - return morphologyExtension -} - -endtemplate SimSettings - - -// Instantiate singleton - load_file() makes sure runs only once -objref simConfig -simConfig = new SimSettings() diff --git a/core/hoc/StimulusManager.hoc b/core/hoc/StimulusManager.hoc index 4f2f8215..dd803cee 100644 --- a/core/hoc/StimulusManager.hoc +++ b/core/hoc/StimulusManager.hoc @@ -9,6 +9,7 @@ {load_file("TStim.hoc")} //note that the TStim functions are used in a way where our naming schemes don't match 100% {load_file("EStim.hoc")} {load_file("fileUtils.hoc")} +{load_file("Map.hoc")} begintemplate StimulusManager diff --git a/core/hoc/neurodamus.hoc b/core/hoc/neurodamus.hoc index f54e02c7..92a83527 100644 --- a/core/hoc/neurodamus.hoc +++ b/core/hoc/neurodamus.hoc @@ -8,9 +8,7 @@ {load_file("binfo.hoc")} // Neurodamus - Do not change order -{load_file("ConfigParser.hoc")} {load_file("RNGSettings.hoc")} -{load_file("ElectrodeManager.hoc")} {load_file("StimulusManager.hoc")} {load_file("Cell.hoc")} {load_file("TDistFunc.hoc")} diff --git a/core/mod/coreneuron_modlist.txt b/core/mod/coreneuron_modlist.txt index e55bb284..5aadbd42 100644 --- a/core/mod/coreneuron_modlist.txt +++ b/core/mod/coreneuron_modlist.txt @@ -1,7 +1,6 @@ ALU.mod CoreNEURONArtificialCell.mod HDF5reader.mod -lookupTableV2.mod netstim_inhpoisson.mod SonataReportHelper.mod SonataReports.mod diff --git a/core/mod/lookupTableV2.mod b/core/mod/lookupTableV2.mod deleted file mode 100755 index 00bf6dfa..00000000 --- a/core/mod/lookupTableV2.mod +++ /dev/null @@ -1,313 +0,0 @@ -COMMENT -/** - * @file lookupTableV2.mod - * @brief - * @author reimann - * @date 2010-12-30 - * @remark Copyright © BBP/EPFL 2005-2011; All rights reserved. Do not distribute without further notice. - */ -ENDCOMMENT - -VERBATIM -#ifndef CORENEURON_BUILD -#include -#include -#include -#include - -typedef struct{ - float **axon; - float **apicals; - float **basals; - int *axonsSz; - int *apicalsSz; - int *basalsSz; - float soma; - unsigned int axonSz; - unsigned int apicalSz; - unsigned int basalSz; -} sectionEntry; - -typedef struct{ - int **gids; - sectionEntry **secData; - float **factors; - int numToRead; - int *tableSizes; - char vInfo; - float *xPos; - float *yPos; - float *zPos; -}tableStruct; - -float swapForFloat(int iAmAFloat){ - float factor; - unsigned char *inPoint = (unsigned char *) &iAmAFloat; - unsigned char *outPoint = (unsigned char *) &factor; - iAmAFloat = (int)htonl(iAmAFloat); - int k; - for(k = 0; k < 4; ++k) - outPoint[k]=inPoint[k]; - return factor; -}; - -void readAndSwapInt(int *readInto, int number, FILE *file){ - int i = 0; - fread(readInto,sizeof(int),number,file); - for(i = 0; i < number; ++i){ - readInto[i] = (int)htonl(readInto[i]); - } -}; -void readAndSwapFloat(float *readInto, int number, FILE *file){ - int i = 0; - int tempReadArray[number]; - fread(&tempReadArray,sizeof(int),number,file); - for(i = 0; i < number; ++i){ - readInto[i] = swapForFloat(tempReadArray[i]); - } -}; -#endif // CORENEURON_BUILD -ENDVERBATIM - -NEURON { - ARTIFICIAL_CELL lookupTableV2 - POINTER ptr -} - -ASSIGNED{ - ptr -} - -CONSTRUCTOR{ - VERBATIM -#ifndef CORENEURON_BUILD - //printf("Building lookup table...\n"); - if(sizeof(float)!=4 || sizeof(int)!=4){ - printf("sizeof does not match. Need to specify data type sizes more explicitly\n"); - return; - } - tableStruct** tempTable = (tableStruct**)(&(_p_ptr)); - tableStruct* tbl = 0; - tbl = (tableStruct*)hoc_Emalloc(sizeof(tableStruct)); - tbl->numToRead = 0; - if(ifarg(1)&&hoc_is_str_arg(1)){ - char tableName[128]; - sprintf(tableName,"%s",gargstr(1)); - FILE *file; - //printf("Opening file: %s\n",tableName); - if((file = fopen(tableName, "r"))==NULL) { - //printf("FAILURE!\n"); - //tbl->numToRead=0; - *tempTable = tbl; - return; - } - float retVal = 0; - //int numToRead; - int i,j,k; - int gidNum; - char header[128] = {0x0}; - char readChar = 0x1; - i=0; - int iAmAFloat; - while(readChar!=0xA){ - fread(&readChar,1,1,file); - header[i++]=readChar; - } - if(strncmp(header,"ExtracellularElectrodeLookupTable",33)!=0){ - *tempTable = tbl; - printf("Header does not match: \n"); - printf("%s", header); - return; - } - fread(&(tbl->vInfo),sizeof(char),1,file); - char circuitPath[512]; - readChar = 0x1; - i=0; - while(readChar!=0xA){ - fread(&readChar,1,1,file); - circuitPath[i++]=readChar; - } - //printf(circuitPath); - fread(&(tbl->numToRead),sizeof(int),1,file); - tbl->numToRead = (int)htonl(tbl->numToRead); - //printf("number: %d\n",tbl->numToRead); - tbl->gids = (int**)hoc_Emalloc((tbl->numToRead)*sizeof(int *)); - tbl->factors = (float**)hoc_Emalloc((tbl->numToRead)*sizeof(float *)); - tbl->secData = (sectionEntry**)hoc_Emalloc((tbl->numToRead)*sizeof(sectionEntry *)); - tbl->tableSizes = (int*)hoc_Emalloc((tbl->numToRead)*sizeof(int)); - tbl->xPos = (float*)hoc_Emalloc((tbl->numToRead)*sizeof(float)); - tbl->yPos = (float*)hoc_Emalloc((tbl->numToRead)*sizeof(float)); - tbl->zPos = (float*)hoc_Emalloc((tbl->numToRead)*sizeof(float)); - //printf("gids: %d; factors: %d; tableSizes: %d\n",tbl->gids,tbl->factors,tbl->tableSizes); - for(i = 0; i < tbl->numToRead; ++i){ - fread((&iAmAFloat),sizeof(float),1,file); - tbl->xPos[i] = swapForFloat(iAmAFloat); - fread((&iAmAFloat),sizeof(float),1,file); - tbl->yPos[i] = swapForFloat(iAmAFloat); - fread((&iAmAFloat),sizeof(float),1,file); - tbl->zPos[i] = swapForFloat(iAmAFloat); - int tableSize; - fread((&tableSize),sizeof(int),1,file); - tableSize = (int)htonl(tableSize); - //printf("tableSize: %d",tableSize); - (tbl->tableSizes)[i]=tableSize; - (tbl->gids)[i] = (int*)hoc_Emalloc(tableSize*sizeof(int)); - (tbl->factors)[i] = (float*)hoc_Emalloc(tableSize*sizeof(float)); - (tbl->secData)[i] = (sectionEntry*)hoc_Emalloc(tableSize*sizeof(sectionEntry)); - if((tbl->gids)[i]==0 || (tbl->factors)[i]==0 || (tbl->secData)[i]==0){ - printf("Problem allocating memory for factor tables\n"); - return; - } - int index; - float factor; - //unsigned char *inPoint = (unsigned char *) &iAmAFloat; - //unsigned char *outPoint = (unsigned char *) &factor; - unsigned int somaSize, axonSize, dendriteSize, apicalSize; - for (j = 0; j < tableSize; ++j){ - fread(&index,4,1,file); - (tbl->gids)[i][j]=(int)htonl(index); - //TODO: byte swapping only needed on little endian systems. - fread((&iAmAFloat),sizeof(iAmAFloat),1,file); //Need to read the float as an int before byte swapping, otherwise the float register might try to "repair" it... - //iAmAFloat = (int)htonl(iAmAFloat); - //for(k = 0; k < 4; ++k) - // outPoint[k]=inPoint[k]; - (tbl->factors)[i][j]=swapForFloat(iAmAFloat); - fread(&somaSize,4,1,file); - somaSize = (unsigned int)htonl(somaSize); - fread(&axonSize,4,1,file); - axonSize = (unsigned int)htonl(axonSize); - (tbl->secData)[i][j].axonSz = axonSize; - fread(&dendriteSize,4,1,file); - dendriteSize = (unsigned int)htonl(dendriteSize); - (tbl->secData)[i][j].basalSz = dendriteSize; - fread(&apicalSize,4,1,file); - apicalSize = (unsigned int)htonl(apicalSize); - (tbl->secData)[i][j].apicalSz = apicalSize; - if(somaSize!=1){ - printf("Need exactly one value for the soma. Got %d",somaSize); - return; - } - - (tbl->secData)[i][j].axonsSz = (int*)hoc_Emalloc(axonSize*sizeof(int)); - (tbl->secData)[i][j].axon = (float**)hoc_Emalloc(axonSize*sizeof(float*)); - (tbl->secData)[i][j].basalsSz = (int*)hoc_Emalloc(dendriteSize*sizeof(int)); - (tbl->secData)[i][j].basals = (float**)hoc_Emalloc(dendriteSize*sizeof(float*)); - (tbl->secData)[i][j].apicalsSz = (int*)hoc_Emalloc(apicalSize*sizeof(int)); - (tbl->secData)[i][j].apicals = (float**)hoc_Emalloc(apicalSize*sizeof(float*)); - - fread(&somaSize,4,1,file); - somaSize = (unsigned int)htonl(somaSize); - if(somaSize!=1){ - printf("Need exactly one value for the soma. Got %d",somaSize); - return; - } - readAndSwapInt((tbl->secData)[i][j].axonsSz,axonSize,file); - readAndSwapInt((tbl->secData)[i][j].basalsSz,dendriteSize,file); - readAndSwapInt((tbl->secData)[i][j].apicalsSz,apicalSize,file); - - fread((&iAmAFloat),sizeof(iAmAFloat),1,file); - (tbl->secData)[i][j].soma = swapForFloat(iAmAFloat); - for(k = 0; k < axonSize; ++k){ - (tbl->secData)[i][j].axon[k] = (float*)hoc_Emalloc((tbl->secData)[i][j].axonsSz[k]*sizeof(float)); - readAndSwapFloat((tbl->secData)[i][j].axon[k],(tbl->secData)[i][j].axonsSz[k],file); - } - for(k = 0; k < dendriteSize; ++k){ - (tbl->secData)[i][j].basals[k] = (float*)hoc_Emalloc((tbl->secData)[i][j].basalsSz[k]*sizeof(float)); - readAndSwapFloat((tbl->secData)[i][j].basals[k],(tbl->secData)[i][j].basalsSz[k],file); - } - for(k = 0; k < apicalSize; ++k){ - (tbl->secData)[i][j].apicals[k] = (float*)hoc_Emalloc((tbl->secData)[i][j].apicalsSz[k]*sizeof(float)); - readAndSwapFloat((tbl->secData)[i][j].apicals[k],(tbl->secData)[i][j].apicalsSz[k],file); - } - } - } - } - *tempTable = tbl; -#endif // CORENEURON_BUILD - ENDVERBATIM -} - -FUNCTION vInfo(){ - VERBATIM -#ifndef CORENEURON_BUILD - tableStruct **tempData = (tableStruct**)(&_p_ptr); - tableStruct *tbl = (tableStruct*) *tempData; - return tbl->vInfo; -#endif // CORENEURON_BUILD - ENDVERBATIM -} - -FUNCTION getValueForGid(){ - VERBATIM -#ifndef CORENEURON_BUILD - tableStruct **tempData = (tableStruct**)(&_p_ptr); - tableStruct *tbl = (tableStruct*) *tempData; - if((tbl->numToRead)==0) - return 1; - float retVal = 0; - int i,j; - if(ifarg(1)){ - int targetGid = (int) *getarg(1); - for(i = 0; i < (tbl->numToRead); ++i){ - for (j = 0; j < (tbl->tableSizes)[i]; ++j){ - if((tbl->gids)[i][j]==targetGid){ - retVal+=(tbl->factors)[i][j]; - break; //Break inner loop. (In the latest specification a gid can only be mentioned once per table.) - } - } - } - } - return retVal; -#endif // CORENEURON_BUILD - ENDVERBATIM -} - -FUNCTION getValueForSection(){ - VERBATIM -#ifndef CORENEURON_BUILD - tableStruct **tempData = (tableStruct**)(&_p_ptr); - tableStruct *tbl = (tableStruct*) *tempData; - if((tbl->numToRead)==0) - return 1; - float retVal = 0; - int i,j; - if(ifarg(4)){ - int targetGid = (int) *getarg(1); - int targetType = (int) *getarg(2); - int targetSec = (int) *getarg(3); - int usedCompIndex; - float compDist = (float) *getarg(4); - for(i = 0; i < (tbl->numToRead); ++i){ //TODO: Argh, use a map or something! - for (j = 0; j < (tbl->tableSizes)[i]; ++j){ - if((tbl->gids)[i][j]==targetGid){ //or directly sort it by gid - sectionEntry tEntry = (tbl->secData)[i][j]; - if(targetType == 3) - if(targetSec Set the number of ModelBuildingSteps for the CoreNeuron sim diff --git a/neurodamus/connection_manager.py b/neurodamus/connection_manager.py index 153717a1..dfcfd1d8 100644 --- a/neurodamus/connection_manager.py +++ b/neurodamus/connection_manager.py @@ -436,7 +436,7 @@ def get_connections(self, post_gids, pre_gids=None, population_ids=None): # - def create_connections(self, src_target=None, dst_target=None): """Creates connections according to loaded parameters in 'Connection' - blocks of the BlueConfig in the currently active ConnectionSet. + blocks of the config in the currently active ConnectionSet. If no Connection block relates to the current population, then load all edges. If a single blocks exists with Weight=0, skip creation entirely. @@ -491,10 +491,10 @@ def create_connections(self, src_target=None, dst_target=None): # - def configure_connections(self, conn_conf): - """Configure-only circuit connections according to a BlueConfig Connection block + """Configure-only circuit connections according to a config Connection block Args: - conn_conf: The BlueConfig configuration block (dict) + conn_conf: The configuration block (dict) """ log_msg = " * Pathway {:s} -> {:s}".format(conn_conf["Source"], conn_conf["Destination"]) @@ -835,7 +835,7 @@ def get_target_connections(self, src_target_name, # - def configure_group(self, conn_config, gidvec=None): - """Configure connections according to a BlueConfig Connection block + """Configure connections according to a config Connection block Args: conn_config: The connection configuration dict @@ -1188,7 +1188,7 @@ class SynapseRuleManager(ConnectionManagerBase): """ The SynapseRuleManager is designed to encapsulate the creation of synapses for BlueBrain simulations, handling the data coming from - the circuit file. If the BlueConfig file provides any Connection + the circuit file. If the config file provides any Connection Rules, those override which synapses are created. Note that the Connection rules are processed with the assumption @@ -1238,7 +1238,7 @@ def setup_delayed_connection(self, conn_config): and add the delay and weight to their delay vectors. Args: - conn_config: Connection configuration parsed from BlueConfig + conn_config: Connection configuration parsed from sonata config """ src_target_name = conn_config["Source"] dst_target_name = conn_config["Destination"] diff --git a/neurodamus/core/configuration.py b/neurodamus/core/configuration.py index 62cdfdf2..8c18ebd9 100644 --- a/neurodamus/core/configuration.py +++ b/neurodamus/core/configuration.py @@ -9,7 +9,6 @@ from collections import defaultdict from enum import Enum -from ..io.config_parser import BlueConfig from ..io.sonata_config import SonataConfig from ..utils import compat from ..utils.logging import log_verbose @@ -29,7 +28,7 @@ class LogLevel: class ConfigurationError(Exception): """ - Error due to invalid settings in BlueConfig + Error due to invalid settings in simulation config ConfigurationError should be raised by all ranks to be caught properly. Otherwise we might end up with deadlocks. For Exceptions that are raised by a single rank Exception @@ -207,12 +206,12 @@ class _SimConfig(object): # Hoc objects used _config_parser = None _parsed_run = None - _blueconfig = None # new python BlueConfig parser + _simulation_config = None _simconf = None rng_info = None # In principle not all vars need to be required as they'r set by the parameter functions - blueconfig_dir = None + simulation_config_dir = None current_dir = None default_neuron_dt = 0.025 buffer_time = 25 @@ -232,7 +231,6 @@ class _SimConfig(object): simulate_model = True loadbal_mode = None synapse_options = {} - is_sonata_config = False spike_location = "soma" spike_threshold = -30 dry_run = False @@ -253,19 +251,15 @@ def init(cls, config_file, cli_options): if not os.path.isfile(config_file): raise ConfigurationError("Config file not found: " + config_file) logging.info("Initializing Simulation Configuration and Validation") - cls.is_sonata_config = config_file.endswith(".json") log_verbose("ConfigFile: %s", config_file) log_verbose("CLI Options: %s", cli_options) cls.config_file = config_file cls._config_parser = cls._init_config_parser(config_file) cls._parsed_run = compat.Map(cls._config_parser.parsedRun) # easy access to hoc Map - if not cls.is_sonata_config: - cls._blueconfig = BlueConfig(config_file) - else: - cls._blueconfig = cls._config_parser # Please refactor me - cls.sonata_circuits = cls._config_parser.circuits - cls.blueconfig_dir = os.path.dirname(os.path.abspath(config_file)) + cls._simulation_config = cls._config_parser # Please refactor me + cls.sonata_circuits = cls._config_parser.circuits + cls.simulation_config_dir = os.path.dirname(os.path.abspath(config_file)) cls.projections = compat.Map(cls._config_parser.parsedProjections) cls.connections = compat.Map(cls._config_parser.parsedConnects) @@ -290,35 +284,17 @@ def init(cls, config_file, cli_options): requisitor(cls, cls._config_parser) logging.info("Initializing hoc config objects") - if not cls.is_sonata_config: - cls._parsed_run.update(run_conf) # sync hoc config cls._init_hoc_config_objs() - @classmethod - def get_blueconfig_hoc_section(cls, section_name): - # Sonata config sections are stored as PyMap, so we need to convert first - if isinstance(cls._config_parser, SonataConfig): - section = getattr(cls._config_parser, section_name) - return section and section.hoc_map - return getattr(cls._config_parser, section_name) - @classmethod def _init_config_parser(cls, config_file): if not config_file.endswith(".json"): - config_parser = cls._init_hoc_config_parser(config_file) # legacy reader - else: + raise ConfigurationError("Invalid configuration file format. " + "The configuration file must be a .json file.") + try: config_parser = SonataConfig(config_file) - if config_parser.parsedRun is None: - raise ConfigurationError("No 'Run' block found in BlueConfig %s", config_file) - return config_parser - - @classmethod - def _init_hoc_config_parser(cls, config_file): - from . import NeurodamusCore as Nd - config_parser = Nd.ConfigParser() - config_parser.open(config_file) - if Nd.pc.id == 0: - config_parser.toggleVerbose() + except Exception as e: + raise ConfigurationError(f"Failed to initialize SonataConfig with {config_file}: {e}") return config_parser @classmethod @@ -326,11 +302,9 @@ def _init_hoc_config_objs(cls): """Init objects which parse/check configs in the hoc world""" from neuron import h parsed_run = cls._parsed_run.hoc_map - cls._simconf = h.simConfig - cls._simconf.interpret(parsed_run) cls.rng_info = h.RNGSettings() - cls.rng_info.interpret(parsed_run) + cls.rng_info.interpret(parsed_run, cls.use_coreneuron) if parsed_run.exists("BaseSeed"): logging.info("User-defined RNG base seed %s", parsed_run.valueOf("BaseSeed")) @@ -414,7 +388,7 @@ def find_input_file(filepath, search_paths=(), alt_filename=None): Raises: (ConfigurationError) If the file could not be found """ - search_paths += (SimConfig.current_dir, SimConfig.blueconfig_dir) + search_paths += (SimConfig.current_dir, SimConfig.simulation_config_dir) def try_find_in(fullpath): if os.path.isfile(fullpath): @@ -452,31 +426,31 @@ def _check_params(section_name, data, required_fields, """ for param in required_fields: if param not in data: - raise ConfigurationError("BlueConfig mandatory param not present: [%s] %s" + raise ConfigurationError("simulation config mandatory param not present: [%s] %s" % (section_name, param)) for param in set(numeric_fields + non_negatives): val = data.get(param) try: val and float(val) except ValueError: - raise ConfigurationError("BlueConfig param must be numeric: [%s] %s" + raise ConfigurationError("simulation config param must be numeric: [%s] %s" % (section_name, param)) for param in non_negatives: val = data.get(param) if val and float(val) < 0: - raise ConfigurationError("BlueConfig param must be positive: [%s] %s" + raise ConfigurationError("simulation config param must be positive: [%s] %s" % (section_name, param)) for param, valid in (valid_values or {}).items(): val = data.get(param) if val and val not in valid: - raise ConfigurationError("BlueConfig param value is invalid: [%s] %s = %s" + raise ConfigurationError("simulation config param value is invalid: [%s] %s = %s" % (section_name, param, val)) for param, deprecated in (deprecated_values or {}).items(): val = data.get(param) if val and val in deprecated: - logging.warning("BlueConfig param value is deprecated: [%s] %s = %s" + logging.warning("simulation config param value is deprecated: [%s] %s = %s" % (section_name, param, val)) @@ -593,7 +567,7 @@ def _extra_circuits(config: _SimConfig, run_conf): from . import EngineBase extra_circuits = {} - for name, circuit_info in config._blueconfig.Circuit.items(): + for name, circuit_info in config._simulation_config.Circuit.items(): log_verbose("CIRCUIT %s (%s)", name, circuit_info.get("Engine", "(default)")) if "Engine" in circuit_info: # Replace name by actual engine @@ -691,13 +665,13 @@ def _spike_parameters(config: _SimConfig, run_conf): @SimConfig.validator def _simulator_globals(config: _SimConfig, run_conf): - if not hasattr(config._blueconfig, "Conditions"): + if not hasattr(config._simulation_config, "Conditions"): return None from neuron import h # Hackish but some constants only live in the helper h.load_file("GABAABHelper.hoc") - for group in config._blueconfig.Conditions.values(): + for group in config._simulation_config.Conditions.values(): for key, value in group.items(): validator = _condition_checks.get(key) if validator: @@ -769,11 +743,10 @@ def _randomize_gaba_risetime(config: _SimConfig, run_conf): @SimConfig.validator def _current_dir(config: _SimConfig, run_conf): curdir = run_conf.get("CurrentDir") - run_conf["BlueConfigDir"] = config.blueconfig_dir if curdir is None: - log_verbose("CurrentDir using BlueConfig path [default]") - curdir = config.blueconfig_dir + log_verbose("CurrentDir using simulation config path [default]") + curdir = config.simulation_config_dir else: if not os.path.isabs(curdir): if curdir == ".": @@ -978,10 +951,6 @@ def _model_building_steps(config: _SimConfig, run_conf): user_config = config.cli_options if user_config.modelbuilding_steps is not None: ncycles = int(user_config.modelbuilding_steps) - src_is_cli = True - elif "ModelBuildingSteps" in run_conf: - ncycles = int(run_conf["ModelBuildingSteps"]) - src_is_cli = False else: return None @@ -996,7 +965,7 @@ def _model_building_steps(config: _SimConfig, run_conf): "Multi-iteration coreneuron data generation requires CircuitTarget") logging.info("Splitting Target for multi-iteration CoreNeuron data generation") - logging.info(" -> Cycles: %d. [src: %s]", ncycles, "CLI" if src_is_cli else "BlueConfig") + logging.info(" -> Cycles: %d. [src: %s]", ncycles, "CLI") config.modelbuilding_steps = ncycles @@ -1037,14 +1006,13 @@ def _spikes_sort_order(config: _SimConfig, run_conf): def get_debug_cell_gid(cli_options): - gid = None if not cli_options else cli_options.get("dump_cell_state") - try: - gid = int(gid) if gid is not None else SimConfig.run_conf.get("prCellGid") - except ValueError as e: - raise ConfigurationError("Cannot parse Gid for dump-cell-state: " + gid) from e - if gid and SimConfig.is_sonata_config: - # In sonata mode, user will provide a 0-based, add 1. - gid += 1 + gid = cli_options.get("dump_cell_state") if cli_options else None + if gid: + try: + # Convert to integer and adjust for sonata mode (0-based to 1-based indexing) + gid = int(gid) + 1 + except ValueError as e: + raise ConfigurationError("Cannot parse Gid for dump-cell-state: " + gid) from e return gid diff --git a/neurodamus/io/config_parser.py b/neurodamus/io/config_parser.py deleted file mode 100644 index 1d93b555..00000000 --- a/neurodamus/io/config_parser.py +++ /dev/null @@ -1,86 +0,0 @@ -""" -Module to parse BlueConfigs -""" -import logging -from collections import defaultdict - - -class BlueConfigParserError(Exception): - pass - - -class BlueConfig: - _known_sections = ( - "Run", "Connection", "Projection", "Stimulus", "StimulusInject", "Report", - "Electrode", "Modification", "NeuronConfigure", "Circuit", "Conditions", - ) - - def __init__(self, filename): - self._sections = defaultdict(dict) - with open(filename) as f: - self._parse_top(f) - if "Run" not in self._sections: - raise BlueConfigParserError("Section 'Run' doesn't exist") - self._sections['Run'] = self._sections['Run']['Default'] - - def _parse_top(self, f): - is_comment = False - for line in f: - line = line.strip() - if not line: - continue - if line[0] == '#': - is_comment = True - continue - if line[0] == '{' and is_comment: - logging.debug("Skipping BlueConfig section %s", line) - self._skip_section(f) - continue - is_comment = False - header_parts = line.split() - if len(header_parts) != 2: - raise BlueConfigParserError("Invalid section header: " + line) - section = header_parts[0] - section_data = self._parse_section(f) - if isinstance(section_data, Exception): - raise BlueConfigParserError( - "Invalid data in section '{}': {}".format(line, str(section_data))) - self._sections[section][header_parts[1]] = section_data - - @staticmethod - def _parse_section(file_iter): - info = {} - next(file_iter) # skip { - for line in file_iter: - line = line.strip() - if not line or line[0] == '#': - continue - if '#' in line: - line = line[:line.index('#')].rstrip() - if line == "}": - break - if line == "{": - return BlueConfigParserError("Section not closed") - parts = line.split(maxsplit=1) - if len(parts) < 2: - return BlueConfigParserError(line) - try: - value = float(parts[1]) - except ValueError: - value = parts[1] - info[parts[0]] = value - return info - - @staticmethod - def _skip_section(file_iter): - for line in file_iter: - line = line.strip(" \t#") - if line.startswith("}"): - break - if line.startswith("{"): - return BlueConfigParserError(line) - - def __getattr__(self, item): - if item not in self._known_sections: - raise KeyError("Config key not supported: " + item) - return self._sections[item] # defaultdict never excepts diff --git a/neurodamus/io/sonata_config.py b/neurodamus/io/sonata_config.py index 915a76f1..b8972155 100644 --- a/neurodamus/io/sonata_config.py +++ b/neurodamus/io/sonata_config.py @@ -37,7 +37,7 @@ class SonataConfig: _config_sections = ( "run", "conditions", "output", "inputs", "reports" ) - # New defaults in Sonata config (not applicable to BlueConfig) + # New defaults in Sonata config _defaults = { "network": "circuit_config.json", } diff --git a/neurodamus/managers.py b/neurodamus/managers.py deleted file mode 100644 index 75db2266..00000000 --- a/neurodamus/managers.py +++ /dev/null @@ -1,86 +0,0 @@ -""" -Structures holding the several instantiated objects from the configuration -""" -import numpy -import logging -from os import path as Path -from .core import NeurodamusCore as Nd - - -class ElectrodeManager(object): - """ Electrode Manager. - Create and handle electrodes according to configuration - """ - __slots__ = ['_pos', '_names', '_electrodes', '_count'] - - def __init__(self, elec_path, elecs_conf): - """Init for ElectrodeManager. - Args: - elec_path: (string) The path where te find the electrodes config - elecs_conf: A python Mapping of the configurations - """ - self._names = [] - self._electrodes = [] - self._count = len(elecs_conf) - self._pos = numpy.empty((self._count, 3), dtype='int32') # x,y,z - cur_i = 0 - - for name, conf in elecs_conf.items(): - el_file = conf.get("File") - if not el_file: - logging.warning("No File speficied for electrode %s. Skipping", name) - continue - el_filepath = Path.join(elec_path, el_file.s) - - # Get electrode, checking versions - v = conf.get("Version") # Either None or hocObject - if v is not None and v.s == "5": - tmp_elec = Nd.lookupTableV2(el_filepath) - v = tmp_elec.vInfo() - if v != 5: - logging.warning("Invalid Electrode version found. Only version 5 is supported") - continue - - # Get & convert xyz - int() base 0 is autodetect (eq to %i) - xyz = [int(conf.get(vname).s, 0) for vname in "xyz"] - - # Store info - self._names.append(name) - self._electrodes.append(tmp_elec) - self._pos[cur_i] = xyz - cur_i += 1 - - def get(self, elec_id): - """Retrieves an electrode object given its name or index. - - Raises: KeyError if the element doesnt exist - """ - if isinstance(elec_id, str): - for i, cur_name in enumerate(self._names): - if cur_name == elec_id: - elec_id = i - break - else: - raise KeyError("ElectrodeManager> Non-existing name: %s" % elec_id) - return self._electrodes[elec_id] - - def find_near(self, x, y, z): - """Retrieves an electrode object given a position. - - Returns: the first Electrode object found within 5 micron (in each direction), - or None if not found - """ - all_dist = numpy.absolute(self._pos - (x, y, z)) - for elec, dist in zip(self._electrodes, all_dist): - if (dist < 5).all(): - return elec - return None - - def get_name(self, i): - return self._names[i] - - def get_position(self, i): - return self._pos[i] - - def __len__(self): - return self._count diff --git a/neurodamus/node.py b/neurodamus/node.py index 2fe67632..3d28e620 100644 --- a/neurodamus/node.py +++ b/neurodamus/node.py @@ -56,7 +56,7 @@ class CircuitManager: Holds and manages populations and associated nodes and edges For backward compat, base population doesnt have a population name (it is '') - All other nodes must have a name, read from sonata pop name, or the BlueConfig circuit + All other nodes must have a name or read from sonata pop name As so, Sonata is preferred when using multiple node files """ @@ -302,7 +302,6 @@ def __init__(self, config_file, options=None): self._stim_list = None self._report_list = None self._stim_manager = None - self._elec_manager = None self._sim_ready = False self._jumpstarters = [] self._cell_state_dump_t = None @@ -317,7 +316,6 @@ def __init__(self, config_file, options=None): circuits = property(lambda self: self._circuits) target_manager = property(lambda self: self._target_manager) stim_manager = property(lambda self: self._stim_manager) - elec_manager = property(lambda self: self._elec_manager) stims = property(lambda self: self._stim_list) reports = property(lambda self: self._report_list) @@ -348,18 +346,16 @@ def compute_load_balance(self): CellDistributor to split cells and balance those pieces across the available CPUs. """ log_stage("Computing Load Balance") - is_sonata_config = SimConfig.is_sonata_config circuit = self._base_circuit - if is_sonata_config: - for name, circuit in self._extra_circuits.items(): - if circuit.get("PopulationType") != "virtual": - break - if circuit.get("PopulationType") == "virtual": - logging.warning( - "Cannot calculate the load balance because only virtual populations were found" - ) - return None - logging.info("Activating experimental LB for Sonata circuit '%s'", name) + for name, circuit in self._extra_circuits.items(): + if circuit.get("PopulationType") != "virtual": + break + if circuit.get("PopulationType") == "virtual": + logging.warning( + "Cannot calculate the load balance because only virtual populations were found" + ) + return None + logging.info("Activating experimental LB for Sonata circuit '%s'", name) if not circuit.CircuitPath: logging.info(" => No circuit for Load Balancing. Skipping... ") @@ -377,15 +373,9 @@ def compute_load_balance(self): return None # Build load balancer as per requested options - # Compat Note: - # data_src in BlueConfig mode was the nrnPath. Not anymore (except if not defined or ) - prosp_hosts = self._run_conf.get("ProspectiveHosts") - data_src = ( - circuit.CircuitPath if is_sonata_config - else self._run_conf["nrnPath"] or circuit.CircuitPath - ) + data_src = circuit.CircuitPath pop = target_spec.population - load_balancer = LoadBalance(lb_mode, data_src, pop, self._target_manager, prosp_hosts) + load_balancer = LoadBalance(lb_mode, data_src, pop, self._target_manager) if load_balancer.valid_load_distribution(target_spec): logging.info("Load Balancing done.") @@ -414,15 +404,6 @@ def create_cells(self, load_balance=None): """Instantiate and distributes the cells of the network. Any targets will be updated to know which cells are local to the cpu. """ - # We wont go further if ProspectiveHosts is defined to some other cpu count - prosp_hosts = self._run_conf.get("ProspectiveHosts") - if load_balance and prosp_hosts not in (None, MPI.size): - logging.warning( - "Load Balance requested for %d CPUs (as per ProspectiveHosts). " - "To continue execution launch on a partition of that size", - prosp_hosts) - Nd.quit(1) - if SimConfig.dry_run: logging.info("Memory usage after inizialization:") print_mem_usage() @@ -661,11 +642,8 @@ def enable_stimulus(self): log_stage("Stimulus Apply.") - # Setup of Electrode objects part of enable stimulus - self._enable_electrodes() - # for each stimulus defined in the config file, request the stimmanager to instantiate - self._stim_manager = StimulusManager(self._target_manager, self._elec_manager) + self._stim_manager = StimulusManager(self._target_manager, None) # build a dictionary of stims for faster lookup : useful when applying 10k+ stims # while we are at it, check if any stims are using extracellular @@ -702,26 +680,10 @@ def enable_stimulus(self): logging.info(" * [STIM] %s: %s (%s) -> %s", name, stim_name, stim_pattern, target_spec) self._stim_manager.interpret(target_spec, stim) - # - - def _enable_electrodes(self): - if SimConfig.use_coreneuron: - # Coreneuron doesnt support electrodes - return False - electrode_path = self._run_conf.get("ElectrodesPath") - if electrode_path is not None: - logging.info("ElectrodeManager using electrodes from %s", electrode_path) - else: - logging.info("No electrodes path. Extracellular class of stimuli will be unavailable") - - self._elec_manager = Nd.ElectrodeManager( - electrode_path and Nd.String(electrode_path), - SimConfig.get_blueconfig_hoc_section("parsedElectrodes") - ) - # - @mpi_no_errors def enable_replay(self): - """Activate replay according to BlueConfig. Call before connManager.finalize + """Activate replay according to config file. Call before connManager.finalize """ if Feature.Replay not in SimConfig.cli_options.restrict_features: logging.warning("Skipped Replay (restrict_features)") @@ -817,7 +779,7 @@ def _coreneuron_replay_append(self, spike_manager, gid_offset=None): @mpi_no_errors @timeit(name="Enable Modifications") def enable_modifications(self): - """Iterate over any Modification blocks read from the BlueConfig and apply them to the + """Iterate over any Modification blocks read from the config file and apply them to the network. The steps needed are more complex than NeuronConfigures, so the user should not be expected to write the hoc directly, but rather access a library of already available mods. """ @@ -840,7 +802,7 @@ def enable_modifications(self): # @mpi_no_errors - not required since theres a call inside before make_comm() @timeit(name="Enable Reports") def enable_reports(self): - """Iterate over reports defined in BlueConfig and instantiate them. + """Iterate over reports defined in the config file and instantiate them. """ log_stage("Reports Enabling") n_errors = 0 @@ -893,10 +855,6 @@ def enable_reports(self): self._reports_init(pop_offsets_alias) - # electrode manager is no longer needed. free the memory - if self._elec_manager is not None: - self._elec_manager.clear() - # def _report_build_params(self, rep_name, rep_conf, target, pop_offsets_alias_pop): sim_end = self._run_conf["Duration"] @@ -941,8 +899,6 @@ def _report_build_params(self, rep_name, rep_conf, target, pop_offsets_alias_pop logging.error("Invalid report dt %f < %f simulation dt", rep_dt, Nd.dt) return None - electrode = self._elec_manager.getElectrode(rep_conf["Electrode"]) \ - if SimConfig.use_neuron and "Electrode" in rep_conf else None rep_target = TargetSpec(rep_conf["Target"]) population_name = (rep_target.population or self._target_spec.population or self._default_population) @@ -959,7 +915,7 @@ def _report_build_params(self, rep_name, rep_conf, target, pop_offsets_alias_pop start_time, end_time, SimConfig.output_root, - electrode, + None, Nd.String(rep_conf["Scaling"]) if "Scaling" in rep_conf else None, rep_conf.get("ISC", "") ) @@ -1068,7 +1024,7 @@ def _reports_init(self, pop_offsets_alias): # - @mpi_no_errors def execute_neuron_configures(self): - """Iterate over any NeuronConfigure blocks from the BlueConfig. + """Iterate over any NeuronConfigure blocks from the config file. These are simple hoc statements that can be executed with minimal substitutions """ printed_warning = False @@ -1240,6 +1196,7 @@ def _coreneuron_ensure_all_ranks_have_gids(self, corenrn_data): # - def _sim_corenrn_configure_datadir(self, corenrn_restore): corenrn_datadir = SimConfig.coreneuron_datadir + os.makedirs(corenrn_datadir, exist_ok=True) corenrn_datadir_shm = SHMUtil.get_datadir_shm(corenrn_datadir) # Clean-up any previous simulations in the same output directory @@ -1326,7 +1283,7 @@ def _sim_corenrn_write_config(self, corenrn_restore=False): # - def run_all(self): - """Run the whole simulation according to BlueConfig + """Run the whole simulation according to the simulation config file """ if not self._sim_ready: self.sim_init() @@ -1612,7 +1569,7 @@ def __init__( * Activate reports if requested Args: - config_file: The BlueConfig recipe file + config_file: The simulation config recipe file logging_level: (int) Redefine the global logging level. 0 - Only warnings / errors 1 - Info messages (default) @@ -1620,7 +1577,7 @@ def __init__( 3 - Debug messages cleanup_atexit: (bool) Call cleanup in the destructor [for more see: https://bbpteam.epfl.ch/project/issues/browse/BBPBGLIB-976] - user_opts: Options to Neurodamus overriding BlueConfig + user_opts: Options to Neurodamus overriding the simulation config file """ self._init_ok = False self.cleanup_atexit = cleanup_atexit @@ -1772,65 +1729,10 @@ def _instantiate_simulation(self): logging.info("MULTI-CYCLE RUN: {} Cycles".format(n_cycles)) TimerManager.archive(archive_name="Before Cycle Loop") - if SimConfig.is_sonata_config: - PopulationNodes.freeze_offsets() - sub_targets = target.generate_subtargets(n_cycles) - - for cycle_i, cur_targets in enumerate(sub_targets): - logging.info("") - logging.info("-" * 60) - log_stage("==> CYCLE {} (OUT OF {})".format(cycle_i + 1, n_cycles)) - logging.info("-" * 60) - - self.clear_model() - - for cur_target in cur_targets: - self._target_manager.register_target(cur_target) - pop = list(cur_target.population_names)[0] - for circuit in itertools.chain([self._base_circuit], - self._extra_circuits.values()): - tmp_target_spec = TargetSpec(circuit.CircuitTarget) - if tmp_target_spec.population == pop: - tmp_target_spec.name = cur_target.name - circuit.CircuitTarget = str(tmp_target_spec) - - self._cycle_i = cycle_i - self._build_model() - - # Move generated files aside (to be merged later) - if MPI.rank == 0: - base_filesdat = ospath.join(SimConfig.coreneuron_datadir, 'files') - os.rename(base_filesdat + '.dat', base_filesdat + "_{}.dat".format(cycle_i)) - # Archive timers for this cycle - TimerManager.archive(archive_name="Cycle Run {:d}".format(cycle_i + 1)) - - else: - self._multi_cycle_run_blueconfig_setting(n_cycles) - - if MPI.rank == 0: - self._merge_filesdat(n_cycles) + PopulationNodes.freeze_offsets() + sub_targets = target.generate_subtargets(n_cycles) - def _multi_cycle_run_blueconfig_setting(self, n_cycles): - """ - Running multi cycle model buildings for blueconfig settings, - Different from sonata setting because for blueconfig target is set per circuit - thus can be different. - This step will be deprecated once the migration to SONATA is complete. - """ - sub_targets = defaultdict(list) - for circuit in itertools.chain([self._base_circuit], self._extra_circuits.values()): - if not circuit.CircuitPath: - continue - if circuit.CircuitTarget is None: - raise ConfigurationError("Multi building steps requires a circuit target " - "for circuit %s" % circuit._name) - - target_spec = TargetSpec(circuit.CircuitTarget) - target = self._target_manager.get_target(target_spec) - circuit_subtargets = target.generate_subtargets(n_cycles) - sub_targets[circuit._name or "Base"] = circuit_subtargets - - for cycle_i in range(n_cycles): + for cycle_i, cur_targets in enumerate(sub_targets): logging.info("") logging.info("-" * 60) log_stage("==> CYCLE {} (OUT OF {})".format(cycle_i + 1, n_cycles)) @@ -1838,14 +1740,15 @@ def _multi_cycle_run_blueconfig_setting(self, n_cycles): self.clear_model() - for circuit_name, circuit_subtargets in sub_targets.items(): - cur_target = circuit_subtargets[cycle_i] - cur_target.name += "_" + circuit_name + for cur_target in cur_targets: self._target_manager.register_target(cur_target) - circuit = self._extra_circuits.get(circuit_name, self._base_circuit) - tmp_target_spec = TargetSpec(circuit.CircuitTarget) - tmp_target_spec.name = cur_target.name - circuit.CircuitTarget = str(tmp_target_spec) + pop = list(cur_target.population_names)[0] + for circuit in itertools.chain([self._base_circuit], + self._extra_circuits.values()): + tmp_target_spec = TargetSpec(circuit.CircuitTarget) + if tmp_target_spec.population == pop: + tmp_target_spec.name = cur_target.name + circuit.CircuitTarget = str(tmp_target_spec) self._cycle_i = cycle_i self._build_model() @@ -1857,6 +1760,9 @@ def _multi_cycle_run_blueconfig_setting(self, n_cycles): # Archive timers for this cycle TimerManager.archive(archive_name="Cycle Run {:d}".format(cycle_i + 1)) + if MPI.rank == 0: + self._merge_filesdat(n_cycles) + # - @timeit(name="finished Run") def run(self): diff --git a/tests/integration/test_sonata_config.py b/tests/integration/test_sonata_config.py index 0b100442..a0793245 100644 --- a/tests/integration/test_sonata_config.py +++ b/tests/integration/test_sonata_config.py @@ -66,7 +66,7 @@ def test_SimConfig_from_sonata(): ) # conditions section - conditions = list(SimConfig._blueconfig.Conditions.values())[0] + conditions = list(SimConfig._simulation_config.Conditions.values())[0] assert conditions['init_depleted_ProbAMPANMDA_EMS'] is False assert conditions['minis_single_vesicle_ProbAMPANMDA_EMS'] is True assert conditions['randomize_Gaba_risetime'] == 'False' diff --git a/tests/simulations/complex.blueconfig b/tests/simulations/complex.blueconfig deleted file mode 100644 index 2a1c3784..00000000 --- a/tests/simulations/complex.blueconfig +++ /dev/null @@ -1,159 +0,0 @@ -Run Default -{ - Date 27:8:19 - Time 11:10:42 - svnPath https://bbpteam.epfl.ch/svn/bluebrain - Version 1094 - - CircuitPath /gpfs/bbp.cscs.ch/project/proj42/circuits/CA1/20190306 - nrnPath /gpfs/bbp.cscs.ch/project/proj42/circuits/CA1/20190306/connectome/functional - MorphologyPath /gpfs/bbp.cscs.ch/project/proj42/entities/morphologies/20180417/ - METypePath /gpfs/bbp.cscs.ch/project/proj42/entities/emodels/20190402/hoc - MEComboInfoFile /gpfs/bbp.cscs.ch/project/proj42/entities/emodels/20190402/mecombo_emodel.tsv - CellLibraryFile circuit.mvd3 - - BioName /gpfs/bbp.cscs.ch/project/proj42/circuits/CA1/20190306/bioname - Atlas /gpfs/bbp.cscs.ch/project/proj42/entities/dev/atlas/20181206-atlas/ - - TargetFile user.target - CurrentDir . - OutputRoot output - - RNGMode UpdatedMCell - #RNGMode Random123 - BaseSeed 10 - - RunMode RR - #CircuitTarget mc2_Column - CircuitTarget selection - - Duration 100 - Dt 0.025 - ForwardSkip 5000 -} - -Projection SC -{ - Path /gpfs/bbp.cscs.ch/project/proj42/circuits/CA1/20190306/projections/singlefile/v3.2k/full_ca1_20190306_v3_2k.sonata - PopulationID 1 -} - -Report soma -{ - Target Mosaic - Type compartment - ReportOn v - Unit mV - Format Bin - Dt 0.5 - StartTime 0 - EndTime 1000 -} - -Stimulus ThresholdExc -{ - - Mode Current - Pattern Noise - MeanPercent 100.457136089 - Variance 0.001 - Delay 0.000000 - Duration 40000.000000 -} - -Stimulus ThresholdInh -{ - - Mode Current - Pattern Noise - MeanPercent 100.457136089 - Variance 0.001 - Delay 0.000000 - Duration 40000.000000 -} - -StimulusInject ThresholdIntoExc -{ - Stimulus ThresholdExc - Target Excitatory -} - -StimulusInject ThresholdIntoInh -{ - Stimulus ThresholdInh - Target Inhibitory -} - -# calculation based on Hajos and Mody, 1997 -Connection Inh-Exc -{ - Source Inhibitory - Destination Excitatory - Weight 1.0 - SpontMinis 0.0077738055338323455 -} - -# calculation based on Hajos and Mody, 1997 -Connection Inh-Inh -{ - Source Inhibitory - Destination Inhibitory - Weight 1.0 - SpontMinis 0.008410333344698649 -} - -# calculation based on Ito and Schuman, 2009 -Connection SC-Exc -{ - Source SC - Destination Excitatory - Weight 1.0 - SpontMinis 0.023678248125348696 -} - -Connection Exc-Exc -{ - Source Excitatory - Destination Excitatory - Weight 1.0 - SpontMinis 0.00015390861281476653 -} - -# calculation based on Zheng et al 2011 (excitatory minis on PV+ interneurons) -Connection SC-Inh -{ - Source SC - Destination Inhibitory - Weight 1.0 - SpontMinis 0.022349689388756172 -} - -Connection Exc-Inh -{ - Source Excitatory - Destination Inhibitory - Weight 1.0 - SpontMinis 0.0014899792925837448 -} - -Connection AMPA_NMDA -{ - Source Excitatory - Destination Mosaic - SynapseConfigure %s.NMDA_ratio = 1.22 tau_r_NMDA_ProbAMPANMDA_EMS = 3.9 tau_d_NMDA_ProbAMPANMDA_EMS = 35.6 -} - -Connection GABA_AB -{ - Source Inhibitory - Destination Mosaic - SynapseConfigure %s.e_GABAA = -80.0 %s.GABAB_ratio = 0 -} # no GABA_B (so far) - -Connection MGGate -{ - Source Excitatory - Destination Mosaic - SynapseConfigure %s.mg = 1.0 -} - diff --git a/tests/simulations/usecase3/BlueConfig b/tests/simulations/usecase3/BlueConfig deleted file mode 100644 index 5d30aece..00000000 --- a/tests/simulations/usecase3/BlueConfig +++ /dev/null @@ -1,69 +0,0 @@ -Run Default -{ - CircuitPath ./ - MorphologyPath CircuitA/morphologies/asc - MorphologyType asc - METypePath CircuitA/hoc - CellLibraryFile nodes_A.h5 - nrnPath local_edges_A.h5 - CircuitTarget Mosaic_A - - Simulator NEURON - RNGMode Random123 - BaseSeed 1 - - CurrentDir . - OutputRoot output - TargetFile user.target - - RunMode RR - Duration 50 - Dt 0.025 -} - - -Circuit B -{ - CircuitPath ./ - MorphologyPath CircuitB/morphologies/asc - MorphologyType asc - METypePath CircuitB/hoc - CellLibraryFile nodes_B.h5 - nrnPath local_edges_B.h5 - CircuitTarget Mosaic_B -} - - -Projection A_to_B -{ - Path edges_AB.h5:NodeA__NodeB__chemical -} - - -#Report soma_SONATA -{ - Target Mosaic_A - Type compartment - ReportOn v - Unit mV - Format SONATA - Dt 1 - StartTime 0 - EndTime 10 -} - - -Stimulus hypamp -{ - Mode Current - Pattern Hyperpolarizing - Delay 0.0 - Duration 4000 -} - - -StimulusInject hypamp_mosaic -{ - Stimulus hypamp - Target Mosaic_A -} diff --git a/tests/simulations/usecase3/user.target b/tests/simulations/usecase3/user.target deleted file mode 100644 index d55e98dd..00000000 --- a/tests/simulations/usecase3/user.target +++ /dev/null @@ -1,9 +0,0 @@ -Target Cell Mosaic_A -{ - a1 a2 a3 -} - -Target Cell Mosaic_B -{ - a1 a2 -} diff --git a/tests/unit/test_blueconfig_parser.py b/tests/unit/test_blueconfig_parser.py deleted file mode 100644 index d1ba568a..00000000 --- a/tests/unit/test_blueconfig_parser.py +++ /dev/null @@ -1,54 +0,0 @@ -import pytest -from neurodamus.io.config_parser import BlueConfig, BlueConfigParserError -from pathlib import Path -from contextlib import contextmanager - -BLUECONFIG_FILE = Path(__file__).parent.parent.absolute() / "simulations" / "complex.blueconfig" - - -def test_blueconfig_parser(): - bc = BlueConfig(BLUECONFIG_FILE) - assert bc.Run['BaseSeed'] == 10 - assert bc.Run['RNGMode'] == "UpdatedMCell" - assert bc.Report['soma']['Dt'] == 0.5 - assert 100.457 < bc.Stimulus['ThresholdInh']['MeanPercent'] < 100.458 - - -@contextmanager -def patch_line(nr, new_line): - original_f = BlueConfig._parse_top - - def parse_top_patch(self, f): - f = list(f) - f[nr] = new_line - original_f(self, iter(f)) - - BlueConfig._parse_top = parse_top_patch - yield - BlueConfig._parse_top = original_f - - -def test_blueconfig_err_no_close(): - with patch_line(32, "# } was here"): - with pytest.raises(BlueConfigParserError, - match="'Run Default': Section not closed"): - BlueConfig(BLUECONFIG_FILE) - - -def test_blueconfig_commented_section(): - with patch_line(40, "# Report soma"): - bc = BlueConfig(BLUECONFIG_FILE) - assert "soma" not in bc.Report - - -def test_blueconfig_err_single_token(): - with patch_line(20, "missing_value"): - with pytest.raises(BlueConfigParserError, - match="Invalid data in section 'Run Default': missing_value"): - BlueConfig(BLUECONFIG_FILE) - - -def test_require_run(): - with patch_line(0, "# Run section commented"): - with pytest.raises(BlueConfigParserError, match="Section 'Run' doesn't exist"): - BlueConfig(BLUECONFIG_FILE)