From 695d75b77518e0eebe863ca0a3b85852689eb159 Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Mon, 9 Mar 2020 21:39:51 +0530 Subject: [PATCH 01/16] saving data to npy format in streamer is now bug free. Added three tests: 2 python and 1 at cpp level. --- basecode/Id.cpp | 16 +- builtins/Streamer.cpp | 42 +-- builtins/StreamerBase.cpp | 41 ++- builtins/StreamerBase.h | 8 +- builtins/Table.cpp | 6 +- pymoose/CMakeLists.txt | 3 +- shell/Neutral.cpp | 4 + shell/Wildcard.cpp | 5 - tests/py_moose/test_streamer.py | 47 ++-- .../py_moose/test_table_streaming_support.py | 80 +++++- utility/CMakeLists.txt | 16 +- utility/cnpy.cpp | 264 ++++++++++++++---- utility/cnpy.hpp | 188 +++---------- utility/test_cnpy.cpp | 64 +++++ 14 files changed, 487 insertions(+), 297 deletions(-) create mode 100644 utility/test_cnpy.cpp diff --git a/basecode/Id.cpp b/basecode/Id.cpp index 6f2e5b1f41..5457ea500b 100644 --- a/basecode/Id.cpp +++ b/basecode/Id.cpp @@ -76,11 +76,18 @@ string Id::id2str( Id id ) string Id::path( const string& separator) const { string ret = Neutral::path( eref() ); - // Trim off trailing [] - assert( ret.length() > 0 ); - // the 'back' operation is not supported by pre 2011 compilers - while ( ret[ ret.length() - 1 ] == ']' ) +#if 0 + // NOTE/FIXME: Monday 09 March 2020 12:30:27 PM IST, Dilawar Singh + // This beaks the path comparison. Getting x.path from x returned in a + // list by moose.wildcardFind() and getting path from here doesn't math + // when this is enabled. + // If we want to remove [0] then it should be done globally and not just + // here. + + // Trim off trailing [] + assert( ret.length() > 0 ); + while ( ret.back() == ']' ) { size_t pos = ret.find_last_of( '[' ); if ( pos != string::npos && pos > 0 ) @@ -88,6 +95,7 @@ string Id::path( const string& separator) const ret = ret.substr( 0, pos ); } } +#endif return ret; } diff --git a/builtins/Streamer.cpp b/builtins/Streamer.cpp index aae926bc7e..b211e92f6e 100644 --- a/builtins/Streamer.cpp +++ b/builtins/Streamer.cpp @@ -185,10 +185,10 @@ void Streamer::reinit(const Eref& e, ProcPtr p) if( tick != tableDt_[0] ) { moose::showWarn( "Table " + tableIds_[i].path() + " has " - " different clock dt. " - " Make sure all tables added to Streamer have the same " - " dt value." - ); + " different clock dt. " + " Make sure all tables added to Streamer have the same " + " dt value." + ); } } } @@ -209,12 +209,12 @@ void Streamer::reinit(const Eref& e, ProcPtr p) if( tableTick_[i] != tableTick_[0] ) { LOG( moose::warning - , "Table " << tableIds_[i].path() - << " has tick (dt) which is different than the first table." - << endl - << " Got " << tableTick_[i] << " expected " << tableTick_[0] - << endl << " Disabling this table." - ); + , "Table " << tableIds_[i].path() + << " has tick (dt) which is different than the first table." + << endl + << " Got " << tableTick_[i] << " expected " << tableTick_[0] + << endl << " Disabling this table." + ); invalidTables.push_back( i ); } } @@ -236,7 +236,7 @@ void Streamer::reinit(const Eref& e, ProcPtr p) // write now. currTime_ = 0.0; zipWithTime( ); - StreamerBase::writeToOutFile(outfilePath_, format_, "w", data_, columns_); + StreamerBase::writeToOutFile(outfilePath_, format_, WRITE, data_, columns_); data_.clear( ); } @@ -247,7 +247,7 @@ void Streamer::reinit(const Eref& e, ProcPtr p) void Streamer::cleanUp( ) { zipWithTime( ); - StreamerBase::writeToOutFile( outfilePath_, format_, "a", data_, columns_ ); + StreamerBase::writeToOutFile( outfilePath_, format_, APPEND, data_, columns_ ); data_.clear( ); } @@ -261,7 +261,7 @@ void Streamer::process(const Eref& e, ProcPtr p) { // LOG( moose::debug, "Writing Streamer data to file." ); zipWithTime( ); - StreamerBase::writeToOutFile( outfilePath_, format_, "a", data_, columns_ ); + StreamerBase::writeToOutFile( outfilePath_, format_, APPEND, data_, columns_ ); data_.clear(); numWriteEvents_ += 1; } @@ -284,12 +284,12 @@ void Streamer::addTable( Id table ) tables_.push_back( t ); tableTick_.push_back( table.element()->getTick() ); - // NOTE: If user can make sure that names are unique in table, using name is - // better than using the full path. - if( t->getColumnName().size() > 0 ) - columns_.push_back( t->getColumnName( ) ); + // NOTE: Table can also have name. If name is set by User, use the name to + // for column name, else use full path of the table. + if(t->getColumnName().size() > 0) + columns_.push_back(t->getColumnName()); else - columns_.push_back( moose::moosePathToUserPath( table.path() ) ); + columns_.push_back(table.path()); } /** @@ -355,7 +355,7 @@ size_t Streamer::getNumTables( void ) const * @Synopsis Get number of write events in streamer. Useful for debugging and * performance measuerments. * - * @Returns + * @Returns */ /* ----------------------------------------------------------------------------*/ size_t Streamer::getNumWriteEvents( void ) const @@ -414,8 +414,8 @@ void Streamer::zipWithTime( ) { #if 0 LOG( moose::debug - , "Table " << tables_[i]->getName( ) << " is not functional. Filling with zero " - ); + , "Table " << tables_[i]->getName( ) << " is not functional. Filling with zero " + ); #endif tVec.resize( numEntriesInEachTable, 0 ); } diff --git a/builtins/StreamerBase.cpp b/builtins/StreamerBase.cpp index 902c3f24c7..53477407fc 100644 --- a/builtins/StreamerBase.cpp +++ b/builtins/StreamerBase.cpp @@ -57,7 +57,7 @@ void StreamerBase::setOutFilepath( string filepath ) void StreamerBase::writeToOutFile( const string& filepath , const string& outputFormat - , const string& openmode + , const OpenMode openmode , const vector& data , const vector& columns ) @@ -65,25 +65,33 @@ void StreamerBase::writeToOutFile( const string& filepath if( data.size() == 0 ) return; - if( "npy" == outputFormat ) - writeToNPYFile( filepath, openmode, data, columns ); + if("npy" == outputFormat || "npz" == outputFormat) + { + OpenMode m = (openmode == WRITE)?WRITE_BIN:APPEND_BIN; + writeToNPYFile( filepath, m, data, columns ); + } else if( "csv" == outputFormat or "dat" == outputFormat ) - writeToCSVFile( filepath, openmode, data, columns ); + { + OpenMode m = (openmode == WRITE)?WRITE_STR:APPEND_STR; + writeToCSVFile( filepath, m, data, columns ); + } else { LOG( moose::warning, "Unsupported format " << outputFormat << ". Use npy or csv. Falling back to default csv" ); - writeToCSVFile( filepath, openmode, data, columns ); + OpenMode m = (openmode == WRITE)?WRITE_STR:APPEND_STR; + writeToCSVFile( filepath, m, data, columns ); } } /* Write to a csv file. */ -void StreamerBase::writeToCSVFile( const string& filepath, const string& openmode +void StreamerBase::writeToCSVFile( const string& filepath, const OpenMode openmode , const vector& data, const vector& columns ) { + string m = (openmode == WRITE_STR)?"w":"a"; + FILE* fp = fopen( filepath.c_str(), m.c_str()); - FILE* fp = fopen( filepath.c_str(), openmode.c_str() ); if( NULL == fp ) { LOG( moose::warning, "Failed to open " << filepath ); @@ -91,7 +99,7 @@ void StreamerBase::writeToCSVFile( const string& filepath, const string& openmod } // If writing in "w" mode, write the header first. - if(openmode == "w") + if(openmode == WRITE_STR) { string headerText = ""; for( vector::const_iterator it = columns.begin(); @@ -116,16 +124,23 @@ void StreamerBase::writeToCSVFile( const string& filepath, const string& openmod } /* write data to a numpy file */ -void StreamerBase::writeToNPYFile( const string& filepath, const string& openmode +void StreamerBase::writeToNPYFile( const string& filepath, const OpenMode openmode , const vector& data, const vector& columns ) { - cnpy2::save_numpy( filepath, data, columns, openmode ); + //for(auto v: data) cout << v << ' '; + //cout << endl; + + if(openmode == APPEND_BIN) + return cnpy2::appendNumpy( filepath, data, columns); + + if(openmode == WRITE_BIN) + return cnpy2::writeNumpy( filepath, data, columns); } string StreamerBase::vectorToCSV( const vector& ys, const string& fmt ) { - stringstream ss; + string res{""}; for( auto v : ys ) - ss << v << ","; - return ss.str(); + res += std::to_string(v) + ","; + return res; } diff --git a/builtins/StreamerBase.h b/builtins/StreamerBase.h index b3e035b1ae..3d29cf9dab 100644 --- a/builtins/StreamerBase.h +++ b/builtins/StreamerBase.h @@ -28,6 +28,8 @@ using namespace std; class TableBase; +enum OpenMode {WRITE, APPEND, WRITE_STR, APPEND_STR, WRITE_BIN, APPEND_BIN}; + class StreamerBase : public TableBase { @@ -63,7 +65,7 @@ class StreamerBase : public TableBase */ static void writeToOutFile( const string& filepath, const string& format - , const string& openmode + , const OpenMode openmode , const vector& data , const vector& columns ); @@ -72,7 +74,7 @@ class StreamerBase : public TableBase * @brief Write data to csv file. See the documentation of writeToOutfile * for details. */ - static void writeToCSVFile( const string& filepath, const string& openmode + static void writeToCSVFile( const string& filepath, const OpenMode openMode , const vector& data, const vector& columns ); @@ -80,7 +82,7 @@ class StreamerBase : public TableBase * @brief Write to NPY format. See the documentation of * writeToOutfile for more details. */ - static void writeToNPYFile( const string& filepath, const string& openmode + static void writeToNPYFile( const string& filepath, const OpenMode openmode , const vector& data , const vector& columns ); diff --git a/builtins/Table.cpp b/builtins/Table.cpp index 4721cf05b0..130a9bf713 100644 --- a/builtins/Table.cpp +++ b/builtins/Table.cpp @@ -228,7 +228,7 @@ Table::~Table( ) if( useFileStreamer_ ) { mergeWithTime( data_ ); - StreamerBase::writeToOutFile( outfile_, format_, "a", data_, columns_ ); + StreamerBase::writeToOutFile( outfile_, format_, APPEND, data_, columns_ ); clearAllVecs(); } } @@ -269,7 +269,7 @@ void Table::process( const Eref& e, ProcPtr p ) if( fmod(lastTime_, 5.0) == 0.0 || getVecSize() >= 10000 ) { mergeWithTime( data_ ); - StreamerBase::writeToOutFile( outfile_, format_, "a", data_, columns_ ); + StreamerBase::writeToOutFile( outfile_, format_, APPEND, data_, columns_ ); clearAllVecs(); } } @@ -332,7 +332,7 @@ void Table::reinit( const Eref& e, ProcPtr p ) if( useFileStreamer_ ) { mergeWithTime( data_ ); - StreamerBase::writeToOutFile( outfile_, format_, "w", data_, columns_); + StreamerBase::writeToOutFile( outfile_, format_, WRITE, data_, columns_); clearAllVecs(); } } diff --git a/pymoose/CMakeLists.txt b/pymoose/CMakeLists.txt index 7d35bd123b..86798d60a8 100644 --- a/pymoose/CMakeLists.txt +++ b/pymoose/CMakeLists.txt @@ -59,8 +59,7 @@ execute_process( COMMAND ${PYTHON_EXECUTABLE}-config --libs set_target_properties(_moose PROPERTIES COMPILE_DEFINITIONS "PYMOOSE" - COMPILE_FLAGS "${COMPILE_FLAGS} ${PYTHON_INCLUDE_FLAGS}" - ) + COMPILE_FLAGS "${COMPILE_FLAGS} ${PYTHON_INCLUDE_FLAGS}") # Remove prefix lib from python module. if(NOT(PYTHON_SO_EXTENSION STREQUAL "")) diff --git a/shell/Neutral.cpp b/shell/Neutral.cpp index 4256ae2187..3e7d448cab 100644 --- a/shell/Neutral.cpp +++ b/shell/Neutral.cpp @@ -764,6 +764,7 @@ string Neutral::path( const Eref& e ) stringstream ss; pathVec.push_back( curr ); + while ( curr.id != Id() ) { ObjId mid = curr.eref().element()->findCaller( pafid ); @@ -776,8 +777,10 @@ string Neutral::path( const Eref& e ) curr = Msg::getMsg( mid )->findOtherEnd( curr ); pathVec.push_back( curr ); } + if ( pathVec.size() <= 1 ) return "/"; + for ( unsigned int i = 1; i < pathVec.size(); ++i ) { ss << "/"; @@ -792,6 +795,7 @@ string Neutral::path( const Eref& e ) ss << "[" << oid.dataIndex << "]"; */ } + // Append braces if Eref was for a fieldElement. This should // work even if it is off-node. if ( e.element()->hasFields() ) diff --git a/shell/Wildcard.cpp b/shell/Wildcard.cpp index abfb4f7de5..675976268c 100644 --- a/shell/Wildcard.cpp +++ b/shell/Wildcard.cpp @@ -104,11 +104,6 @@ static int innerFind( const string& path, vector< ObjId >& ret) return wildcardRelativeFind( start, names, 0, ret ); } -/* -static int wildcardRelativeFind( Id start, const vector< string >& path, - unsigned int depth, vector< Id >& ret ) - */ - /** * This is the basic wildcardFind function, working on a single * tree. It adds entries into the vector 'ret' with Ids found according diff --git a/tests/py_moose/test_streamer.py b/tests/py_moose/test_streamer.py index ca51659d54..b5d243c6ab 100644 --- a/tests/py_moose/test_streamer.py +++ b/tests/py_moose/test_streamer.py @@ -66,7 +66,9 @@ def test_sanity( ): st.removeTables( [a, a, c] ) assert st.numTables == 1 -def test_streamer( ): +def buildSystem(outfile): + if moose.exists('/compt'): + moose.delete('/compt') compt = moose.CubeMesh( '/compt' ) assert compt r = moose.Reac( '/compt/r' ) @@ -82,10 +84,6 @@ def test_streamer( ): r.Kf = 0.1 r.Kb = 0.01 - outfile = 'streamer_test.csv' - if os.path.exists(outfile): - os.remove(outfile) - tabA = moose.Table2( '/compt/a/tab' ) tabB = moose.Table2( '/compt/tabB' ) tabC = moose.Table2( '/compt/tabB/tabC' ) @@ -103,33 +101,28 @@ def test_streamer( ): st.addTable( tabA ) st.addTables( [ tabB, tabC ] ) assert st.numTables == 3 + return st +def test_abit_more(): + stCSV = buildSystem('data.csv') moose.reinit( ) - t = 100 - print( '[INFO] Running for %d seconds' % t ) - moose.start(t) - outfile = st.outfile - moose.quit() # Otherwise Streamer won't flush the rest of entries. - - print('Moose is done. Waiting for monitor to shut down...') - - # Now read the table and verify that we have written - print( '[INFO] Reading file %s' % outfile ) - if 'csv' in outfile: - data = np.loadtxt(outfile, skiprows=1 ) - else: - data = np.load( outfile ) - # Total rows should be 58 (counting zero as well). - # print(data) - # print( data.dtype ) - assert data.shape >= (101,), data.shape - print( '[INFO] Test 2 passed' ) - return 0 + moose.start(100) + csvData = np.loadtxt(stCSV.outfile, skiprows=1) + + stNumpy = buildSystem('data.npy') + moose.reinit() + moose.start(100) + npData = np.load(stNumpy.outfile) + + assert csvData.shape[0] == npData.shape[0], npData.shape + assert csvData.shape[1] == len(npData.dtype) # cols + + for i, name in enumerate(npData.dtype.names): + assert (csvData[:,i] == npData[name]).all() def main( ): test_sanity( ) - test_streamer( ) - print( '[INFO] All tests passed' ) + test_abit_more( ) if __name__ == '__main__': main() diff --git a/tests/py_moose/test_table_streaming_support.py b/tests/py_moose/test_table_streaming_support.py index 7334186547..fa3809da8f 100644 --- a/tests/py_moose/test_table_streaming_support.py +++ b/tests/py_moose/test_table_streaming_support.py @@ -27,8 +27,8 @@ def print_table( table ): msg += ' Path: %s' % table.path print( msg ) -def test( ): - compt = moose.CubeMesh( '/compt' ) +def test_small( ): + moose.CubeMesh( '/compt' ) r = moose.Reac( '/compt/r' ) a = moose.Pool( '/compt/a' ) a.concInit = 1 @@ -69,10 +69,82 @@ def test( ): c = np.loadtxt( 'tablec.csv', skiprows=1 ) assert (len(a) == len(b) == len(c)) +def buildLargeSystem(useStreamer = False): + # create a huge system. + if moose.exists('/comptB'): + moose.delete('/comptB') + moose.CubeMesh( '/comptB' ) + + tables = [] + for i in range(300): + r = moose.Reac('/comptB/r%d'%i) + a = moose.Pool('/comptB/a%d'%i) + a.concInit = 10.0 + b = moose.Pool('/comptB/b%d'%i) + b.concInit = 2.0 + c = moose.Pool('/comptB/c%d'%i) + c.concInit = 0.5 + moose.connect( r, 'sub', a, 'reac' ) + moose.connect( r, 'prd', b, 'reac' ) + moose.connect( r, 'prd', c, 'reac' ) + r.Kf = 0.1 + r.Kb = 0.01 + + # Make table name large enough such that the header is larger than 2^16 + # . Numpy version 1 can't handle such a large header. If format 1 is + # then this test will fail. + t = moose.Table2('/comptB/TableO1%d'%i + 'abc'*100) + moose.connect(t, 'requestOut', a, 'getConc') + tables.append(t) + + if useStreamer: + s = moose.Streamer('/comptB/streamer') + s.outfile = 'data2.npy' + print("[INFO ] Total tables %d" % len(tables)) + + # Add tables using wilcardFind. + s.addTables(moose.wildcardFind('/##[TYPE=Table2]')) + + print("Streamer has %d table" % s.numTables) + assert s.numTables == len(tables) + + moose.reinit() + moose.start(10) + + if useStreamer: + # load the data + data = np.load(s.outfile) + header = str(data.dtype.names) + assert len(header) > 2**16 + else: + data = { x.path : x.vector for x in tables } + return data + +def test_large_system(): + # Get data without streamer and with streamer. + # These two must be the same. + X = buildLargeSystem(False) # without streamer + Y = buildLargeSystem(True) # with streamer. + + # X has no time. + assert len(X) == len(Y.dtype.names)-1, (len(X), Y.dtype) + + # same column names. + xNames = list(X.keys()) + yNames = list(Y.dtype.names) + assert set(yNames) - set(xNames) == set(['time']) + + # Test for equality in some tables. + for i in range(1, 10): + a, b = Y[xNames[i]], X[xNames[i]] + assert a.shape == b.shape, (a.shape, b.shape) + assert (a == b).all(), (a-b) + + def main( ): - test( ) + # test_small( ) + test_large_system() print( '[INFO] All tests passed' ) - if __name__ == '__main__': main() diff --git a/utility/CMakeLists.txt b/utility/CMakeLists.txt index 9a79f1dcbb..fe069b8d82 100644 --- a/utility/CMakeLists.txt +++ b/utility/CMakeLists.txt @@ -1,13 +1,6 @@ cmake_minimum_required(VERSION 2.8) include( ${CMAKE_CURRENT_SOURCE_DIR}/../CheckCXXCompiler.cmake) -IF(WITH_BOOST) -include(CheckIncludeFiles) -check_include_files( ${Boost_INCLUDE_DIRS}/boost/random/random_device.hpp - BOOST_RANDOM_DEVICE_EXISTS - ) -endif(WITH_BOOST) - add_library(utility strutil.cpp types.cpp @@ -19,3 +12,12 @@ add_library(utility fileutils.cpp utility.cpp ) + +add_executable(test_cnpy test_cnpy.cpp) +target_link_libraries(test_cnpy utility) + +set(TEST_SCRIPT ${CMAKE_BINARY_DIR}/test_cnpy.py) +file(WRITE ${TEST_SCRIPT} "import numpy as np") + +enable_testing() +add_test(NAME cpp_test_cnpy COMMAND $) diff --git a/utility/cnpy.cpp b/utility/cnpy.cpp index 131df10ce2..05fea0a385 100644 --- a/utility/cnpy.cpp +++ b/utility/cnpy.cpp @@ -17,48 +17,26 @@ */ #include "cnpy.hpp" -#include +#include +#include + #include "print_function.hpp" using namespace std; -namespace cnpy2 { +namespace cnpy2 +{ // Check the endian-ness of machine at run-time. This is from library // https://github.com/rogersce/cnpy -char BigEndianTest() { +char BigEndianTest() +{ unsigned char x[] = {1,0}; short y = *(short*) x; return y == 1 ? '<' : '>'; } -// And another function to convert given std datatype to numpy representation. -char map_type(const std::type_info& t) -{ - if(t == typeid(float) ) return 'f'; - if(t == typeid(double) ) return 'd'; - if(t == typeid(long double) ) return 'd'; - - if(t == typeid(int) ) return 'i'; - if(t == typeid(char) ) return 'i'; - if(t == typeid(short) ) return 'i'; - if(t == typeid(long) ) return 'i'; - if(t == typeid(long long) ) return 'i'; - - if(t == typeid(unsigned char) ) return 'u'; - if(t == typeid(unsigned short) ) return 'u'; - if(t == typeid(unsigned long) ) return 'u'; - if(t == typeid(unsigned long long) ) return 'u'; - if(t == typeid(unsigned int) ) return 'u'; - - if(t == typeid(bool) ) return 'b'; - - if(t == typeid(std::complex) ) return 'c'; - if(t == typeid(std::complex) ) return 'c'; - if(t == typeid(std::complex) ) return 'c'; - - else return '?'; -} +size_t headerSize = 0; void split(vector& strs, string& input, const string& pat) { @@ -72,6 +50,26 @@ void split(vector& strs, string& input, const string& pat) delete pch; } +string shapeToString(const vector& shape) +{ + string s{"("}; + if(! shape.empty()) + { + s += std::to_string(shape[0]); + for(size_t i = 1; i < shape.size(); i++) + { + s += ","; + s += std::to_string(shape[i]); + } + if(shape.size() == 1) + s += ","; + } + else + s += "0,"; + s += ")"; + return s; +} + /** * @brief Check if a numpy file is sane or not. * @@ -81,18 +79,18 @@ void split(vector& strs, string& input, const string& pat) * * @return true if file is sane, else false. */ -bool is_valid_numpy_file( FILE* fp ) +bool isValidNumpyFile( FILE* fp ) { assert( fp ); - char buffer[__pre__size__]; - size_t nr = fread( buffer, sizeof(char), __pre__size__, fp ); + char buffer[__pre__.size()]; + size_t nr = fread( buffer, sizeof(char), __pre__.size(), fp ); if( 0 == nr ) return false; bool equal = true; // Check for equality - for(size_t i = 0; i < __pre__size__; i++ ) + for(size_t i = 0; i < __pre__.size(); i++ ) if( buffer[i] != __pre__[i] ) { equal = false; @@ -106,18 +104,20 @@ bool is_valid_numpy_file( FILE* fp ) * * @param header */ -void parse_header( FILE* fp, string& header ) +void findHeader(std::fstream& fs, string& header ) { // Read header, till we hit newline character. char ch; + fs.seekg(0); // Go to the begining. header.clear(); - while( ( ch = fgetc( fp )) != EOF ) + while(! fs.eof()) { + fs.get(ch); if( '\n' == ch ) break; - header.push_back( ch ); + header.push_back(ch); } - assert( header.size() >= __pre__size__ ); + assert( header.size() >= __pre__.size() ); } /** @@ -127,21 +127,13 @@ void parse_header( FILE* fp, string& header ) * @param data_len * @param */ -void change_shape_in_header( const string& filename - , const size_t data_len, const size_t numcols - ) +void changeHeaderShape(std::fstream& fs, const size_t data_len, const size_t numcols) { - string header; - - // Always open file in r+b mode. a+b mode always append at the end. - FILE* fp = fopen( filename.c_str(), "r+b" ); - if( ! fp ) - { - moose::showWarn( "Failed to open " + filename ); - return; - } + string header{""}; - parse_header( fp, header ); + // Find header. Search for newline. + findHeader(fs, header); + const size_t headerSize = header.size(); size_t shapePos = header.find( "'shape':" ); size_t lbrac = header.find( '(', shapePos ); @@ -152,25 +144,181 @@ void change_shape_in_header( const string& filename string prefixHeader = header.substr( 0, lbrac + 1 ); string postfixHeader = header.substr( rbrac ); - string shapeStr = header.substr( lbrac + 1, rbrac - lbrac - 1); + string shapeStr = header.substr( lbrac+1, rbrac-lbrac-1); vector tokens; split( tokens, shapeStr, "," ); string newShape = ""; for (size_t i = 0; i < tokens.size(); i++) - newShape += moose::toString( atoi( tokens[i].c_str() ) + data_len/numcols ) + ","; + newShape += std::to_string( atoi( tokens[i].c_str() ) + data_len/numcols ) + ","; - string newHeader = prefixHeader + newShape + postfixHeader + "\n"; + string newHeader = prefixHeader + newShape + postfixHeader; if( newHeader.size() < header.size() ) { cout << "Warn: Modified header can not be smaller than old header" << endl; } - // Move to at the begining of file and write the new header. - fseek(fp, 0, SEEK_SET); - fwrite( newHeader.c_str(), sizeof(char), newHeader.size(), fp ); - fclose( fp ); + // Resize to the old header size. Newline is not included in the header. + newHeader.resize(header.size()); + newHeader += '\n'; // Add newline before writing. + fs.seekp(0); + fs.write(newHeader.c_str(), newHeader.size()); +} + +size_t writeHeader(std::fstream& fs, const vector& colnames, const vector& shape) +{ + // Heder are always at the begining of file. + fs.seekp(0); + + // Write the format string. 8 bytes. + fs.write(&__pre__[0], __pre__.size()); + + char endianChar = cnpy2::BigEndianTest(); + const char formatChar = 'd'; + + // Next 4 bytes are header length. This is computed again when data is + // appended to the file. We can have maximum of 2^32 bytes of header which + // ~4GB. + + string header = ""; // This is the header to numpy file + header += "{'descr':["; + for( auto it = colnames.cbegin(); it != colnames.end(); it++ ) + header += "('" + *it + "','" + endianChar + formatChar + "'),"; + + // shape is changed everytime we append the data. We use fixed number of + // character in shape. Its a int, we will use 13 chars to represent shape. + header += "], 'fortran_order':False,'shape':"; + header += shapeToString(shape); + header += ",}"; + + // Add some extra sapce for safety. + header += string(12, ' '); + + // FROM THE DOC: It is terminated by a newline (\n) and padded with spaces + // (\x20) to make the total of len(magic string) + 2 + len(length) + + // HEADER_LEN be evenly divisible by 64 for alignment purposes. + // pad with spaces so that preamble+headerlen+header is modulo 16 bytes. + // preamble is 8 bytes, header len is 4 bytes, total 12. + // header needs to end with \n + unsigned int remainder = 16 - (12 + header.size()) % 16; + header.insert(header.end(), remainder-1, ' '); + header += '\n'; // Add newline. + + // Now write the size of header. Its 4 byte long in version 2. + uint32_t s = header.size(); + fs.write((char*)&s, 4); + fs << header; + return fs.tellp(); +} + + +size_t initNumpyFile(const string& outfile, const vector& colnames) +{ + std::fstream fs; + fs.open(outfile, std::fstream::in | std::fstream::out | std::fstream::trunc | std::ofstream::binary); + + if(! fs.is_open()) + { + cerr << "Error: Could not create " << outfile << endl; + return 0; + } + vector shape; + auto pos = writeHeader(fs, colnames, shape); + fs.close(); + return pos; +} + +void writeNumpy(const string& outfile, const vector& data, const vector& colnames) +{ + + // In our application, we need to write a vector as matrix. We do not + // support the stacking of matrices. + vector shape; + + if( colnames.size() == 0) + return; + + shape.push_back(data.size() / colnames.size()); + + // Create a new file. + std::fstream fs; + fs.open(outfile, std::ofstream::in | std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); + + /* In mode "w", open the file and write a header as well. When file is open + * in mode "a", we assume that file is alreay a valid numpy file. + */ + if(! fs.is_open()) + { + moose::showWarn( "Could not open file " + outfile ); + return; + } + + auto p = writeHeader( fs, colnames, shape ); + fs.seekp(p); + + // Here the previous character is '\n' + + fs.write(reinterpret_cast(data.data()), sizeof(double)*data.size()); + fs.close(); +} + +void appendNumpy(const string& outfile, const vector& vec, const vector& colnames) +{ + + std::fstream fs; + fs.open(outfile, std::fstream::in | std::fstream::out | std::fstream::binary); + + if( ! fs.is_open() ) + { + moose::showWarn( "Could not open " + outfile + " to write " ); + return; + } + + // And change the shape in header. + changeHeaderShape(fs, vec.size(), colnames.size()); + + // Go to the end. + fs.seekp(0, std::ios_base::end); + fs.write(reinterpret_cast(&vec[0]), sizeof(double)*vec.size()); + fs.close(); +} + +void readNumpy(const string& infile, vector& data) +{ + cout << "Reading from " << infile << endl; + std::ifstream fs; + fs.open(infile, std::ios::in | std::ios::binary); + + if(! fs.is_open()) + { + cerr << "Could not open " << infile << endl; + return; + } + + char ch; + fs.get(ch); + size_t nBytes = 1; + while(ch != '\n') + { + fs.get(ch); + nBytes += 1; + } + + char buff[sizeof(double)]; + double x; + + fs.seekg(nBytes, std::ios_base::beg); + while(! fs.eof()) + { + fs.read(buff, sizeof(double)); + if(fs.gcount() != 8) + break; + memcpy(&x, buff, sizeof(double)); + data.push_back(x); + } + cout << endl; + fs.close(); } } /* Namespace cnpy2 ends. */ diff --git a/utility/cnpy.hpp b/utility/cnpy.hpp index b7148beb0c..f49e32e2d6 100644 --- a/utility/cnpy.hpp +++ b/utility/cnpy.hpp @@ -28,32 +28,23 @@ #include #include -#ifdef ENABLE_CPP11 #include #include -#else /* ----- not ENABLE_CPP11 ----- */ -#endif /* ----- not ENABLE_CPP11 ----- */ - #include - #include -#include "../basecode/global.h" - #include "../utility/print_function.hpp" using namespace std; -namespace cnpy2 { +namespace cnpy2 +{ // Check the endian-ness of machine at run-time. This is from library // https://github.com/rogersce/cnpy char BigEndianTest(); -// And another function to convert given std datatype to numpy representation. -char map_type(const std::type_info& t); - void split(vector& strs, string& input, const string& pat); /** @@ -65,14 +56,14 @@ void split(vector& strs, string& input, const string& pat); * * @return true if file is sane, else false. */ -bool is_valid_numpy_file( FILE* fp ); +bool isValidNumpyFile(std::ifstream& fp); /** * @brief Parser header from a numpy file. Store it in vector. * * @param header */ -void parse_header( FILE* fp, string& header ); +void findHeader(std::fstream& fp, string& header ); /** * @brief Change shape in numpy header. @@ -81,146 +72,43 @@ void parse_header( FILE* fp, string& header ); * @param data_len * @param */ -void change_shape_in_header( const string& filename - , const size_t data_len, const size_t numcols - ); +void changeHeaderShape(std::ofstream& fs, const size_t data_len, const size_t numcols); -static const unsigned int __pre__size__ = 8; -static char __pre__[__pre__size__] = { +// Use version 2.0 of npy fommat. +// https://numpy.org/devdocs/reference/generated/numpy.lib.format.html +static vector __pre__ { (char)0x93, 'N', 'U', 'M', 'P', 'Y' /* Magic */ - , (char)0x01, (char) 0x00 /* format */ + , (char)0x02, (char) 0x00 /* format */ }; -template< typename T> -void write_header( - FILE* fp - , const vector& colnames - , vectorshape , char version - ) -{ - // Heder are always at the begining of file. - fseek( fp, 0, SEEK_SET ); - char endianChar = cnpy2::BigEndianTest(); - char formatChar = cnpy2::map_type( typeid(T) ); - - string dict = ""; // This is the header to numpy file - dict += "{'descr': ["; - for( vector::const_iterator it = colnames.begin(); - it != colnames.end(); it++ ) - dict += "('" + *it + "' , '" + endianChar + formatChar + "'),"; - - dict += "], 'fortran_order': False, 'shape': ("; - dict += moose::toString(shape[0]); - for(size_t i = 1; i < shape.size(); i++) - { - dict += ","; - dict += moose::toString(shape[i]); - } - if( shape.size() == 1) dict += ","; - dict += "), }"; - - // When appending to this file, we need to rewrite header. Size of header - // might change since shape values might change. Minimum shape from (1,) - // could become quite large (13132131321,) etc. For safety region, append a - // chunk of whitespace characters so that overwriting header does not - // overwrite the data. This trick would save us from copying data into - // memory. - dict += string(11, ' '); /* 32 bit number is fit */ - - // pad with spaces so that preamble+headerlen+dict is modulo 16 bytes. - // preamble is 8 bytes, header len is 4 bytes, total 12. - // dict needs to end with \n - unsigned int remainder = 16 - (12 + dict.size()) % 16; - dict.insert(dict.end(),remainder,' '); - *(dict.end()-1) = '\n'; - - if( version == '2' ) - __pre__[6] = (char) 0x02; - - fwrite( __pre__, sizeof( char ), __pre__size__, fp ); - - // Now write the size of dict. It is 2bytes long in version 1 and 4 bytes - // long in version 2. - if( version == '2' ) - { - uint32_t s = dict.size(); - fwrite( (char*)&s, sizeof( uint32_t ), 1, fp ); - } - else - { - int16_t s = dict.size(); - fwrite( (char*)&s, sizeof( uint16_t ), 1, fp ); - } - fwrite( dict.c_str(), sizeof(char), dict.size(), fp ); -} - -// write to version 1 or version 2. -template -void save_numpy( - const string& outfile - , const vector& vec - , vector colnames - , const string openmode - , const char version = '1' - ) -{ +size_t writeHeader(std::fstream& fp, const vector& colnames, const vector& shape); + +void writeNumpy(const string& outfile, const vector& vec, const vector& colnames); + +void appendNumpy(const string& outfile, const vector& vec, const vector& colnames); + +/* --------------------------------------------------------------------------*/ +/** + * @Synopsis initialize a numpy file with given column names. + * + * @Param filename + * @Param colnames + */ + +size_t initNumpyFile(const string& outfile, const vector& colnames); + + +/* --------------------------------------------------------------------------*/ +/** + * @Synopsis read numpy file and return data in a vector. This for testing + * purpose and should not used to read data. + * + * @Param infile + * @Param data + */ +/* ----------------------------------------------------------------------------*/ +void readNumpy(const string& infile, vector& data); + +} // Namespace cnpy2 ends. - // In our application, we need to write a vector as matrix. We do not - // support the stacking of matrices. - vector shape; - - if( colnames.size() == 0) - return; - - shape.push_back( vec.size() / colnames.size()); - - /* In mode "w", open the file and write a header as well. When file is open - * in mode "a", we assume that file is alreay a valid numpy file. - */ - if( openmode == "w" ) - { - FILE* fp = fopen( outfile.c_str(), "wb" ); - if( NULL == fp ) - { - moose::showWarn( "Could not open file " + outfile ); - return; - } - write_header( fp, colnames, shape, version ); - fclose( fp ); - } - else /* Append mode. */ - { - // Do a sanity check if file is really a numpy file. - FILE* fp = fopen( outfile.c_str(), "r" ); - if( ! fp ) - { - moose::showError( "Can't open " + outfile + " to validate" ); - return; - } - else if(! is_valid_numpy_file( fp ) ) - { - moose::showWarn( outfile + " is not a valid numpy file" - + " I am not goind to write to it" - ); - return; - } - if( fp ) - fclose( fp ); - // And change the shape in header. - change_shape_in_header( outfile, vec.size(), colnames.size() ); - } - - FILE* fp = fopen( outfile.c_str(), "ab" ); - if( NULL == fp ) - { - moose::showWarn( "Could not open " + outfile + " to write " ); - return; - } - fwrite( &vec[0], sizeof(T), vec.size(), fp ); - fclose( fp ); - -} - - -} /* Namespace cnpy2 ends. */ #endif /* ----- #ifndef cnpy_INC ----- */ diff --git a/utility/test_cnpy.cpp b/utility/test_cnpy.cpp new file mode 100644 index 0000000000..b17e3070d2 --- /dev/null +++ b/utility/test_cnpy.cpp @@ -0,0 +1,64 @@ +// ===================================================================================== +// +// Filename: test_cnpy.cpp +// +// Description: Test cnpy. +// +// Version: 1.0 +// Created: Monday 09 March 2020 01:22:51 IST +// Revision: none +// Compiler: g++ +// +// Author: Dilawar Singh (), dilawar.s.rajput@gmail.com +// Organization: NCBS Bangalore +// +// ===================================================================================== + +#include "cnpy.hpp" + +#include +#include + +using namespace std; + + +int main(int argc, const char *argv[]) +{ + srand(time(NULL)); + + string datafile = "_a_data.npy"; + vector cols {"A", "B", "C", "P", "Q" }; + + // Now append data. + vector data; + for (size_t i = 0; i < cols.size(); i++) + for (size_t ii = 0; ii < 10; ii++) + data.push_back(rand() / (double)RAND_MAX); + + cout << "Size of data is " << data.size() << endl; + + cnpy2::writeNumpy(datafile, data, cols); + vector r1; + cnpy2::readNumpy(datafile, r1); + cout << "Size of data without append: " << r1.size() << endl; + for (size_t i = 0; i < r1.size(); i++) + assert(r1[i] == data[i]); + + + datafile = "_b_data.npy"; + cnpy2::initNumpyFile(datafile, cols); + cnpy2::appendNumpy(datafile, data, cols); + cnpy2::appendNumpy(datafile, data, cols); + cnpy2::appendNumpy(datafile, data, cols); + + vector r2; + cnpy2::readNumpy(datafile, r2); + + cout << "Total data read " << r2.size() << endl; + assert(r2.size() == 3 * data.size() ); + + for (size_t i = 0; i < r2.size(); i++) + assert(data[i%data.size()] == r2[i]); + + return 0; +} From 182b705af9242b60dfd9a0dbd095623fb411e02c Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Tue, 10 Mar 2020 14:24:42 +0530 Subject: [PATCH 02/16] boost::filesystem is no longer used even when boost is used. --- basecode/Id.cpp | 4 +- basecode/global.cpp | 396 ++++++++++-------- basecode/global.h | 376 +++++++++-------- builtins/Streamer.cpp | 43 +- builtins/Streamer.h | 6 +- builtins/Table.cpp | 77 ++-- builtins/Table.h | 52 +-- shell/Neutral.cpp | 4 - .../py_moose/test_table_streaming_support.py | 23 +- 9 files changed, 504 insertions(+), 477 deletions(-) diff --git a/basecode/Id.cpp b/basecode/Id.cpp index 5457ea500b..5cdd445945 100644 --- a/basecode/Id.cpp +++ b/basecode/Id.cpp @@ -77,8 +77,7 @@ string Id::path( const string& separator) const { string ret = Neutral::path( eref() ); -#if 0 - // NOTE/FIXME: Monday 09 March 2020 12:30:27 PM IST, Dilawar Singh + // FIXME: Monday 09 March 2020 12:30:27 PM IST, Dilawar Singh // This beaks the path comparison. Getting x.path from x returned in a // list by moose.wildcardFind() and getting path from here doesn't math // when this is enabled. @@ -95,7 +94,6 @@ string Id::path( const string& separator) const ret = ret.substr( 0, pos ); } } -#endif return ret; } diff --git a/basecode/global.cpp b/basecode/global.cpp index c5035f45db..6cca169009 100644 --- a/basecode/global.cpp +++ b/basecode/global.cpp @@ -18,10 +18,11 @@ #include "global.h" #include +#include + #include #include - /*----------------------------------------------------------------------------- * This variable keep track of how many tests have been performed. * @@ -35,220 +36,251 @@ bool isRNGInitialized = false; clock_t simClock = clock(); -extern int checkPath( const string& path); -extern string joinPath( string pathA, string pathB); -extern string fixPath( string path); -extern string dumpStats( int ); +extern int checkPath(const string& path); +extern string joinPath(string pathA, string pathB); +extern string fixPath(string path); +extern string dumpStats(int); -namespace moose { +namespace moose +{ - unsigned long __rng_seed__ = 0; +unsigned long __rng_seed__ = 0; - map> solverProfMap = { - { "Ksolve", {0.0, 0} }, - { "HSolve", {0.0, 0} } - }; +map> solverProfMap = {{"Ksolve", {0.0, 0}}, + {"HSolve", {0.0, 0}} +}; - moose::RNG rng; +moose::RNG rng; - /* Check if path is OK */ - int checkPath( const string& path ) - { - if( path.size() < 1) - return EMPTY_PATH; +/* Check if path is OK */ +int checkPath(const string& path) +{ + if (path.size() < 1) + return EMPTY_PATH; - if( path.find_first_of( " \\!") != std::string::npos ) - return BAD_CHARACTER_IN_PATH; - - if ( path[path.size() - 1 ] != ']') - { - return MISSING_BRACKET_AT_END; - } - return 0; - } + if (path.find_first_of(" \\!") != std::string::npos) + return BAD_CHARACTER_IN_PATH; - /* Join paths */ - string joinPath( string pathA, string pathB ) + if (path[path.size() - 1] != ']') { - pathA = moose::fixPath( pathA ); - string newPath = pathA + "/" + pathB; - return moose::fixPath( newPath ); + return MISSING_BRACKET_AT_END; } + return 0; +} - /* Fix given path */ - string fixPath(string path) - { - int pathOk = moose::checkPath( path ); - if( pathOk == 0) - return path; - else if( pathOk == MISSING_BRACKET_AT_END) - return path + "[0]"; +/* Join paths */ +string joinPath(string pathA, string pathB) +{ + pathA = moose::fixPath(pathA); + string newPath = pathA + "/" + pathB; + return moose::fixPath(newPath); +} + +/* Fix given path */ +string fixPath(string path) +{ + int pathOk = moose::checkPath(path); + if (pathOk == 0) return path; - } + else if (pathOk == MISSING_BRACKET_AT_END) + return path + "[0]"; + return path; +} - /** - * @brief Set the global seed or all rngs. - * - * @param x - */ - void mtseed( unsigned int x ) - { - moose::__rng_seed__ = x; - moose::rng.setSeed( x ); - isRNGInitialized = true; - } +/** + * @brief Set the global seed or all rngs. + * + * @param x + */ +void mtseed(unsigned int x) +{ + moose::__rng_seed__ = x; + moose::rng.setSeed(x); + isRNGInitialized = true; +} - /* Generate a random number */ - double mtrand( void ) - { - return moose::rng.uniform( ); - } +/* Generate a random number */ +double mtrand(void) +{ + return moose::rng.uniform(); +} - double mtrand( double a, double b ) - { - return (b-a) * mtrand() + a; - } +double mtrand(double a, double b) +{ + return (b - a) * mtrand() + a; +} + +// MOOSE suffixes [0] to all elements to path. Remove [0] with null +// character whenever possible. For n > 0, [n] should not be touched. Its +// the user job to take the pain and write the correct path. +string createMOOSEPath(const string& path) +{ + string s = path; /* Local copy */ + // Remove [0] from paths. They will be annoying for normal users. + std::string::size_type n = 0; + string zeroIndex("[0]"); + while ((n = s.find(zeroIndex, n)) != std::string::npos) + s.erase(n, zeroIndex.size()); + return s; +} + +/** + * @brief Create directories recursively needed to open the given file p. + * + * @param path When successfully created, returns created path, else + * convert path to a filename by replacing '/' by '_'. + */ +bool createParentDirs(const string& path) +{ + // Remove the filename from the given path so we only have the + // directory. + string p = path; + bool failed = false; + size_t pos = p.find_last_of('/'); + if (pos != std::string::npos) + p = p.substr(0, pos); + else /* no parent directory to create */ + return true; - // MOOSE suffixes [0] to all elements to path. Remove [0] with null - // character whenever possible. For n > 0, [n] should not be touched. Its - // the user job to take the pain and write the correct path. - string createMOOSEPath( const string& path ) + if (p.size() == 0) + return true; + + string command("mkdir -p "); + command += p; + int ret = system(command.c_str()); + struct stat info; + if (stat(p.c_str(), &info) != 0) { - string s = path; /* Local copy */ - // Remove [0] from paths. They will be annoying for normal users. - std::string::size_type n = 0; - string zeroIndex("[0]"); - while( (n = s.find( zeroIndex, n )) != std::string::npos ) - s.erase( n, zeroIndex.size() ); - return s; + LOG(moose::warning, "cannot access " << p); + return false; } - - /** - * @brief Create directories recursively needed to open the given file p. - * - * @param path When successfully created, returns created path, else - * convert path to a filename by replacing '/' by '_'. - */ - bool createParentDirs( const string& path ) + else if (info.st_mode & S_IFDIR) { - // Remove the filename from the given path so we only have the - // directory. - string p = path; - bool failed = false; - size_t pos = p.find_last_of( '/' ); - if( pos != std::string::npos ) - p = p.substr( 0, pos ); - else /* no parent directory to create */ - return true; - - if( p.size() == 0 ) - return true; - -#ifdef USE_BOOST_FILESYSTEM - try - { - boost::filesystem::path pdirs( p ); - boost::filesystem::create_directories( pdirs ); - LOG( moose::info, "Created directory " << p ); - return true; - } - catch(const boost::filesystem::filesystem_error& e) - { - LOG( moose::warning, "create_directories(" << p << ") failed with " - << e.code().message() - ); - return false; - } -#else /* ----- not USE_BOOST_FILESYSTEM ----- */ - string command( "mkdir -p "); - command += p; - int ret = system( command.c_str() ); - struct stat info; - if( stat( p.c_str(), &info ) != 0 ) - { - LOG( moose::warning, "cannot access " << p ); - return false; - } - else if( info.st_mode & S_IFDIR ) - { - LOG( moose::info, "Created directory " << p ); - return true; - } - else - { - LOG( moose::warning, p << " is no directory" ); - return false; - } -#endif /* ----- not USE_BOOST_FILESYSTEM ----- */ + LOG(moose::info, "Created directory " << p); return true; } - - - /* Flatten a dir-name to return a filename which can be created in pwd . */ - string toFilename( const string& path ) + else { - string p = path; - std::replace(p.begin(), p.end(), '/', '_' ); - std::replace(p.begin(), p.end(), '\\', '_' ); - return p; + LOG(moose::warning, p << " is no directory"); + return false; } + return true; +} - /* return extension of a filename */ - string getExtension(const string& path, bool without_dot ) - { - size_t dotPos = path.find_last_of( '.' ); - if( dotPos == std::string::npos ) - return ""; +/* Flatten a dir-name to return a filename which can be created in pwd . */ +string toFilename(const string& path) +{ + string p = path; + std::replace(p.begin(), p.end(), '/', '_'); + std::replace(p.begin(), p.end(), '\\', '_'); + return p; +} - if( without_dot ) - return path.substr( dotPos + 1 ); +/* return extension of a filename */ +string getExtension(const string& path, bool without_dot) +{ + size_t dotPos = path.find_last_of('.'); + if (dotPos == std::string::npos) + return ""; - return path.substr( dotPos ); - } + if (without_dot) + return path.substr(dotPos + 1); - /* returns `basename path` */ - string pathToName( const string& path ) - { - return path.substr( path.find_last_of( '/' ) ); - } + return path.substr(dotPos); +} - /* /a[0]/b[1]/c[0] -> /a/b/c */ - string moosePathToUserPath( string path ) - { - // Just write the moose path. Things becomes messy when indexing is - // used. - return createMOOSEPath( path ); - } +/* returns `basename path` */ +string pathToName(const string& path) +{ + return path.substr(path.find_last_of('/')); +} - /* Return formatted string - * Precision is upto 17 decimal points. - */ - string toString( double x ) - { - char buffer[50]; - sprintf(buffer, "%.17g", x ); - return string( buffer ); - } +/* /a[0]/b[1]/c[0] -> /a/b/c */ +string moosePathToUserPath(string path) +{ + // Just write the moose path. Things becomes messy when indexing is + // used. + return createMOOSEPath(path); +} - int getGlobalSeed( ) +/* --------------------------------------------------------------------------*/ +/** + * @Synopsis Generate a suitable column name based on given path. Replace + * [\d+] with \d+; and remove [\0+]. Replace / with given delim (default '.') + * + * @Param path (string). + * @Param delim (char) + * @Param maxLevel (int). Max number of parents to include. Default 1. + * + * @Returns Reformatted path suitable for column name. + * + * Example: + * ------- + * /a[12]/d[0]/e[1]/d[0] -> e1.d + * /a[12]/d[0] -> e12.d + * /a[00]/b[0] -> a.b + */ +/* ----------------------------------------------------------------------------*/ +string moosePathToColumnName(const string& path, char delim, size_t maxParents) +{ + string s(path); + static std::regex e0("\\[(0+)\\]"); // Remove [0+] + static std::regex e1("\\[(\\d+)\\]"); // Replace [xyz] by xyz + s = std::regex_replace(s, e0, ""); + s = std::regex_replace(s, e1, "$1"); + + string s2; + string colname = ""; + size_t nBreak = 0; + + // Keep as many parents as required by maxParents. Using reverse magic. + for (auto rit = s.rbegin(); rit != s.rend(); rit++) { - return __rng_seed__; + if (*rit == '/') + { + colname = s2 + delim + colname; + s2 = ""; + nBreak += 1; + if (nBreak == (1 + maxParents)) + break; + } + else + s2 = *rit + s2; } + colname.pop_back(); + return colname; +} - void setGlobalSeed( int seed ) - { - __rng_seed__ = seed; - } +/* Return formatted string + * Precision is upto 17 decimal points. + */ +string toString(double x) +{ + char buffer[50]; + sprintf(buffer, "%.17g", x); + return string(buffer); +} - void addSolverProf( const string& name, double time, size_t steps) - { - solverProfMap[ name ] = solverProfMap[name] + valarray({ time, (double)steps }); - } +int getGlobalSeed() +{ + return __rng_seed__; +} - void printSolverProfMap( ) - { - for( auto &v : solverProfMap ) - cout << '\t' << v.first << ": " << v.second[0] << " sec (" << v.second[1] << ")" << endl; - } +void setGlobalSeed(int seed) +{ + __rng_seed__ = seed; +} +void addSolverProf(const string& name, double time, size_t steps) +{ + solverProfMap[name] = + solverProfMap[name] + valarray({time, (double)steps}); +} + +void printSolverProfMap() +{ + for (auto& v : solverProfMap) + cout << '\t' << v.first << ": " << v.second[0] << " sec (" + << v.second[1] << ")" << endl; +} } diff --git a/basecode/global.h b/basecode/global.h index 76ca1cdd75..992b562baa 100644 --- a/basecode/global.h +++ b/basecode/global.h @@ -8,21 +8,15 @@ ** See the file COPYING.LIB for the full notice. **********************************************************************/ - -#ifndef __MOOSE_GLOBAL_INC_ -#define __MOOSE_GLOBAL_INC_ +#ifndef __MOOSE_GLOBAL_INC_ +#define __MOOSE_GLOBAL_INC_ #include #include #include #include - -#ifdef USE_BOOST_FILESYSTEM -#include -#endif - -#include "randnum/RNG.h" /* Use inbuilt rng */ +#include "../randnum/RNG.h" /* Use inbuilt rng */ #include "../utility/print_function.hpp" using namespace std; @@ -37,23 +31,24 @@ extern stringstream errorSS; */ extern unsigned int totalTests; - /** @brief This macro prints the output of a test function onto console. It * also keep track of index of the current test. The index of test is * automatically computed by increamenting the counter. */ -#define TEST_BEGIN cout << endl << "Test(" << totalTests << "): " << SIMPLE_CURRENT_FUNCTION; -#define TEST_END totalTests++; \ - cout << std::right << setw(20) << "test of " << SIMPLE_CURRENT_FUNCTION << " finished."; - -#define MISSING_BRACKET_AT_END -1 -#define EMPTY_PATH -2 -#define SPACES_AT_THE_BEGINING -3 -#define SPACES_AT_THE_END -4 -#define SPACES_IN_BETWEEN -5 -#define BAD_CHARACTER_IN_PATH -6 +#define TEST_BEGIN \ + cout << endl << "Test(" << totalTests << "): " << SIMPLE_CURRENT_FUNCTION; +#define TEST_END \ + totalTests++; \ + cout << std::right << setw(20) << "test of " << SIMPLE_CURRENT_FUNCTION \ + << " finished."; +#define MISSING_BRACKET_AT_END -1 +#define EMPTY_PATH -2 +#define SPACES_AT_THE_BEGINING -3 +#define SPACES_AT_THE_END -4 +#define SPACES_IN_BETWEEN -5 +#define BAD_CHARACTER_IN_PATH -6 /*----------------------------------------------------------------------------- * Global functions in namespace moose @@ -61,171 +56,180 @@ extern unsigned int totalTests; namespace moose { - extern moose::RNG rng; - - extern map> solverProfMap; - - /** - * @brief A global seed for all RNGs in moose. When moose.seed( x ) is called, - * this variable is set. Other's RNGs (except muparser) uses this seed to - * initialize them. By default it is initialized by random_device (see - * global.cpp). - */ - extern unsigned long __rng_seed__; - - /** - * @brief Fix a path. For testing purpose. - * - * @param path Path as string. - * - * @return A fixed path. - */ - string fixPath(string path); - - /** - * @brief Checks if given path is correct. - * If not, return false and error-code as well. - * - * @param path Path name. - * - * @return 0 if path is all-right. Negative number if path is not OK. - */ - int checkPath( const string& path ); - - /** @brief Append pathB to pathA and return the result. - * - * If pathA does not have [indexs] at the end, append "[0]" to pathA and - * then add pathB to it. This version does not care if the result has '[0]' - * at its end. - * - * @param pathA First path. - * @param pathB Second path. - * - * @return A string representing moose-path. - */ - string joinPath(string pathA, string pathB); - - /** - * @brief Seed seed for RNG. - * - * @param seed - */ - void mtseed( unsigned int seed ); - - /** - * @brief Generate a random double between 0 and 1 - * - * @return A random number between 0 and 1. - */ - double mtrand( void ); - - /* --------------------------------------------------------------------------*/ - /** - * @Synopsis Overloaded function. Random number between a and b - * - * @Param a lower limit. - * @Param b Upper limit. - * - * @Returns - */ - /* ----------------------------------------------------------------------------*/ - double mtrand( double a, double b ); - - /** - * @brief Create a POSIX compatible path from a given string. - * Remove/replace bad characters. - * - * @param path Reutrn path is given path if creation was successful, else - * directory is renamed to a filename. - */ - string createMOOSEPath( const string& path ); - - /** - * @brief Convert a given value to string. - * - * @tparam T - * @param x - * - * @return String representation - */ - string toString( double x ); - - /** - * @brief Create directory, recursively. - */ - bool createParentDirs( const string& path ); - - /** - * @brief Replace all directory sepearator with _. This creates a filepath - * which can be created in current directory without any need to create - * parent directory. - * - * @param path string - * - * @return filename without directory separator. - */ - string toFilename( const string& path ); - - /** - * @brief Get the extension of a given filepath. - * - * @param path Given path. - * - * @return Extension (with or without preceeding '.' ) - */ - string getExtension( const string& path, bool without_dot = true ); - - /** - * @brief Return the name when path is given. Its behaviour is exactly the - * same as of `basename` command on unix system. - * - * @param path - * - * @return name. - */ - string pathToName( const string& path ); - - /** - * @brief When user gives a path /a/b/c, moose creates a path - * /a[0]/b[0]/c[0]. This is helpful in cases where one needs to create more - * than 1 element. - * - * @param path Removed '[0]' from path and return. - * - * @return - */ - string moosePathToUserPath( string path ); - - /* --------------------------------------------------------------------------*/ - /** - * @Synopsis Get the global seed set by call of moose.seed( X ) - * - * @Returns seed (int). - */ - /* ----------------------------------------------------------------------------*/ - int getGlobalSeed( ); - - /* --------------------------------------------------------------------------*/ - /** - * @Synopsis Set the seed for all random generator. When seed of a RNG is - * not set, this seed it used. It is set to -1 by default. - * - * @Param seed - */ - /* ----------------------------------------------------------------------------*/ - void setGlobalSeed( int seed ); - - /* --------------------------------------------------------------------------*/ - /** - * @Synopsis Add solver performance into the global map. - * - * @Param name Name of the solver. - * @Param time Time taken by the solver. - * @Param steps Steps. - */ - /* ----------------------------------------------------------------------------*/ - void addSolverProf( const string& name, double time, size_t steps = 1); - void printSolverProfMap( ); +extern moose::RNG rng; + +extern map> solverProfMap; + +/** + * @brief A global seed for all RNGs in moose. When moose.seed( x ) is called, + * this variable is set. Other's RNGs (except muparser) uses this seed to + * initialize them. By default it is initialized by random_device (see + * global.cpp). + */ +extern unsigned long __rng_seed__; + +/** + * @brief Fix a path. For testing purpose. + * + * @param path Path as string. + * + * @return A fixed path. + */ +string fixPath(string path); + +/** + * @brief Checks if given path is correct. + * If not, return false and error-code as well. + * + * @param path Path name. + * + * @return 0 if path is all-right. Negative number if path is not OK. + */ +int checkPath(const string& path); + +/** @brief Append pathB to pathA and return the result. + * + * If pathA does not have [indexs] at the end, append "[0]" to pathA and + * then add pathB to it. This version does not care if the result has '[0]' + * at its end. + * + * @param pathA First path. + * @param pathB Second path. + * + * @return A string representing moose-path. + */ +string joinPath(string pathA, string pathB); + +/** + * @brief Seed seed for RNG. + * + * @param seed + */ +void mtseed(unsigned int seed); + +/** + * @brief Generate a random double between 0 and 1 + * + * @return A random number between 0 and 1. + */ +double mtrand(void); + +/* --------------------------------------------------------------------------*/ +/** + * @Synopsis Overloaded function. Random number between a and b + * + * @Param a lower limit. + * @Param b Upper limit. + * + * @Returns + */ +/* ----------------------------------------------------------------------------*/ +double mtrand(double a, double b); + +/** + * @brief Create a POSIX compatible path from a given string. + * Remove/replace bad characters. + * + * @param path Reutrn path is given path if creation was successful, else + * directory is renamed to a filename. + */ +string createMOOSEPath(const string& path); + +/** + * @brief Convert a given value to string. + * + * @tparam T + * @param x + * + * @return String representation + */ +string toString(double x); + +/** + * @brief Create directory, recursively. + */ +bool createParentDirs(const string& path); + +/** + * @brief Replace all directory sepearator with _. This creates a filepath + * which can be created in current directory without any need to create + * parent directory. + * + * @param path string + * + * @return filename without directory separator. + */ +string toFilename(const string& path); + +/** + * @brief Get the extension of a given filepath. + * + * @param path Given path. + * + * @return Extension (with or without preceeding '.' ) + */ +string getExtension(const string& path, bool without_dot = true); + +/** + * @brief Return the name when path is given. Its behaviour is exactly the + * same as of `basename` command on unix system. + * + * @param path + * + * @return name. + */ +string pathToName(const string& path); + +/** + * @brief When user gives a path /a/b/c, moose creates a path + * /a[0]/b[0]/c[0]. This is helpful in cases where one needs to create more + * than 1 element. + * + * @param path Removed '[0]' from path and return. + * + * @return + */ +string moosePathToUserPath(string path); + +/* --------------------------------------------------------------------------*/ +/** + * @Synopsis Genearate a suitable string for column name which can be used + * in a spreadsheet/numpy table/csv file etc. + */ +/* ----------------------------------------------------------------------------*/ +string moosePathToColumnName(const string& path, char delim = '.', + size_t maxParents = 1); + +/* --------------------------------------------------------------------------*/ +/** + * @Synopsis Get the global seed set by call of moose.seed( X ) + * + * @Returns seed (int). + */ +/* ----------------------------------------------------------------------------*/ +int getGlobalSeed(); + +/* --------------------------------------------------------------------------*/ +/** + * @Synopsis Set the seed for all random generator. When seed of a RNG is + * not set, this seed it used. It is set to -1 by default. + * + * @Param seed + */ +/* ----------------------------------------------------------------------------*/ +void setGlobalSeed(int seed); + +/* --------------------------------------------------------------------------*/ +/** + * @Synopsis Add solver performance into the global map. + * + * @Param name Name of the solver. + * @Param time Time taken by the solver. + * @Param steps Steps. + */ +/* ----------------------------------------------------------------------------*/ +void addSolverProf(const string& name, double time, size_t steps = 1); +void printSolverProfMap(); } -#endif /* ----- #ifndef __MOOSE_GLOBAL_INC_ ----- */ +#endif /* ----- #ifndef __MOOSE_GLOBAL_INC_ ----- */ diff --git a/builtins/Streamer.cpp b/builtins/Streamer.cpp index b211e92f6e..ce8e144c14 100644 --- a/builtins/Streamer.cpp +++ b/builtins/Streamer.cpp @@ -21,14 +21,22 @@ const Cinfo* Streamer::initCinfo() /*----------------------------------------------------------------------------- * Finfos *-----------------------------------------------------------------------------*/ - static ValueFinfo< Streamer, string > outfile( - "outfile" + static ValueFinfo< Streamer, string > datafile( + "datafile" , "File/stream to write table data to. Default is is __moose_tables__.dat.n" " By default, this object writes data every second \n" - , &Streamer::setOutFilepath - , &Streamer::getOutFilepath + , &Streamer::setDatafilePath + , &Streamer::getDatafilePath ); + static ValueFinfo< Streamer, string > outfile( + "outfile" + , "Use datafile (deprecated)" + , &Streamer::setDatafilePath + , &Streamer::getDatafilePath + ); + + static ValueFinfo< Streamer, string > format( "format" , "Format of output file, default is csv" @@ -104,7 +112,7 @@ const Cinfo* Streamer::initCinfo() static Finfo * tableStreamFinfos[] = { - &outfile, &format, &proc, &numTables, &numWriteEvents + &datafile, &outfile, &format, &proc, &numTables, &numWriteEvents }; static string doc[] = @@ -229,14 +237,14 @@ void Streamer::reinit(const Eref& e, ProcPtr p) if( ! isOutfilePathSet_ ) { string defaultPath = "_tables/" + moose::moosePathToUserPath( e.id().path() ); - setOutFilepath( defaultPath ); + setDatafilePath( defaultPath ); } // Prepare data. Add columns names and write whatever values are available // write now. currTime_ = 0.0; zipWithTime( ); - StreamerBase::writeToOutFile(outfilePath_, format_, WRITE, data_, columns_); + StreamerBase::writeToOutFile(datafilePath_, format_, WRITE, data_, columns_); data_.clear( ); } @@ -247,7 +255,7 @@ void Streamer::reinit(const Eref& e, ProcPtr p) void Streamer::cleanUp( ) { zipWithTime( ); - StreamerBase::writeToOutFile( outfilePath_, format_, APPEND, data_, columns_ ); + StreamerBase::writeToOutFile( datafilePath_, format_, APPEND, data_, columns_ ); data_.clear( ); } @@ -261,7 +269,7 @@ void Streamer::process(const Eref& e, ProcPtr p) { // LOG( moose::debug, "Writing Streamer data to file." ); zipWithTime( ); - StreamerBase::writeToOutFile( outfilePath_, format_, APPEND, data_, columns_ ); + StreamerBase::writeToOutFile( datafilePath_, format_, APPEND, data_, columns_ ); data_.clear(); numWriteEvents_ += 1; } @@ -285,11 +293,12 @@ void Streamer::addTable( Id table ) tableTick_.push_back( table.element()->getTick() ); // NOTE: Table can also have name. If name is set by User, use the name to - // for column name, else use full path of the table. + // for column name, else use modify the table path to generate a suitable + // column name. if(t->getColumnName().size() > 0) columns_.push_back(t->getColumnName()); else - columns_.push_back(table.path()); + columns_.push_back(moose::moosePathToColumnName(table.path())); } /** @@ -364,19 +373,19 @@ size_t Streamer::getNumWriteEvents( void ) const } -string Streamer::getOutFilepath( void ) const +string Streamer::getDatafilePath( void ) const { - return outfilePath_; + return datafilePath_; } -void Streamer::setOutFilepath( string filepath ) +void Streamer::setDatafilePath( string filepath ) { - outfilePath_ = filepath; + datafilePath_ = filepath; isOutfilePathSet_ = true; if( ! moose::createParentDirs( filepath ) ) - outfilePath_ = moose::toFilename( outfilePath_ ); + datafilePath_ = moose::toFilename( datafilePath_ ); - string format = moose::getExtension( outfilePath_, true ); + string format = moose::getExtension( datafilePath_, true ); if( format.size() > 0) setFormat( format ); else diff --git a/builtins/Streamer.h b/builtins/Streamer.h index 3136211fce..34876332d7 100644 --- a/builtins/Streamer.h +++ b/builtins/Streamer.h @@ -42,8 +42,8 @@ class Streamer : public StreamerBase /* Cleaup before quitting */ void cleanUp( void ); - string getOutFilepath( void ) const; - void setOutFilepath( string path ); + string getDatafilePath( void ) const; + void setDatafilePath( string path ); string getFormat( void ) const; void setFormat( string format ); @@ -73,7 +73,7 @@ class Streamer : public StreamerBase private: - string outfilePath_; + string datafilePath_; string format_; size_t numWriteEvents_; diff --git a/builtins/Table.cpp b/builtins/Table.cpp index 130a9bf713..d36d564e23 100644 --- a/builtins/Table.cpp +++ b/builtins/Table.cpp @@ -10,6 +10,7 @@ #include "../basecode/header.h" #include "../basecode/global.h" #include +#include #include "TableBase.h" #include "Table.h" @@ -64,14 +65,22 @@ const Cinfo* Table::initCinfo() , &Table::getUseSpikeMode ); - static ValueFinfo< Table, string > outfile( - "outfile" + static ValueFinfo< Table, string > datafile( + "datafile" , "Set the name of file to which data is written to. If set, " " streaming support is automatically enabled." - , &Table::setOutfile - , &Table::getOutfile + , &Table::setDatafile + , &Table::getDatafile + ); + + static ValueFinfo< Table, string > outfile( + "outfile" + , "Use datafile (deprecated)" + , &Table::setDatafile + , &Table::getDatafile ); + static ValueFinfo< Table, string > format( "format" , "Data format for table: default csv" @@ -134,9 +143,10 @@ const Cinfo* Table::initCinfo() &threshold, // Value &format, // Value &columnName, // Value + &datafile, // Value &outfile, // Value &useStreamer, // Value - &useSpikeMode, // Value + &useSpikeMode, // Value handleInput(), // DestFinfo &spike, // DestFinfo requestOut(), // SrcFinfo @@ -212,14 +222,12 @@ Table::Table() : input_( 0.0 ), fired_(false), useSpikeMode_(false), - dt_( 0.0 ) + dt_( 0.0 ), + lastN_(0), + useFileStreamer_(false), + datafile_(""), + format_("csv") { - // Initialize the directory to which each table should stream. - rootdir_ = "_tables"; - useFileStreamer_ = false; - format_ = "csv"; - outfileIsSet_ = false; - lastN_ = 0; } Table::~Table( ) @@ -228,7 +236,8 @@ Table::~Table( ) if( useFileStreamer_ ) { mergeWithTime( data_ ); - StreamerBase::writeToOutFile( outfile_, format_, APPEND, data_, columns_ ); + assert( ! datafile_.empty() ); + StreamerBase::writeToOutFile( datafile_, format_, APPEND, data_, columns_); clearAllVecs(); } } @@ -269,7 +278,7 @@ void Table::process( const Eref& e, ProcPtr p ) if( fmod(lastTime_, 5.0) == 0.0 || getVecSize() >= 10000 ) { mergeWithTime( data_ ); - StreamerBase::writeToOutFile( outfile_, format_, APPEND, data_, columns_ ); + StreamerBase::writeToOutFile( datafile_, format_, APPEND, data_, columns_ ); clearAllVecs(); } } @@ -294,23 +303,25 @@ void Table::reinit( const Eref& e, ProcPtr p ) unsigned int numTick = e.element()->getTick(); Clock* clk = reinterpret_cast(Id(1).eref().data()); + dt_ = clk->getTickDt( numTick ); fired_ = false; + // Set column name for this table. It is used in Streamer to generate + // column names because path can be pretty verbose and name may not be + // unique. This is the default column name. + if( tableColumnName_.empty() ) + tableColumnName_ = moose::moosePathToColumnName(tablePath_); + + columns_ = {"time", tableColumnName_}; + /** Create the default filepath for this table. */ if( useFileStreamer_ ) { - // The first column is variable time. - columns_.push_back( "time" ); - // And the second column name is the name of the table. - columns_.push_back( moose::moosePathToUserPath( tablePath_ ) ); - // If user has not set the filepath, then use the table path prefixed // with rootdit as path. - if( ! outfileIsSet_ ) - setOutfile( rootdir_ + - moose::moosePathToUserPath(tablePath_) + '.' + format_ - ); + if( datafile_.empty() ) + setDatafile(moose::moosePathToUserPath(tablePath_) + '.' + format_); } input_ = 0.0; @@ -332,7 +343,7 @@ void Table::reinit( const Eref& e, ProcPtr p ) if( useFileStreamer_ ) { mergeWithTime( data_ ); - StreamerBase::writeToOutFile( outfile_, format_, WRITE, data_, columns_); + StreamerBase::writeToOutFile(datafile_, format_, WRITE, data_, columns_); clearAllVecs(); } } @@ -428,25 +439,23 @@ bool Table::getUseSpikeMode( void ) const } -/* set/get outfile_ */ -void Table::setOutfile( string outpath ) +/* set/get datafile_ */ +void Table::setDatafile( string filepath ) { - outfile_ = moose::createMOOSEPath( outpath ); - if( ! moose::createParentDirs( outfile_ ) ) - outfile_ = moose::toFilename( outfile_ ); + datafile_ = moose::createMOOSEPath( filepath ); + if( ! moose::createParentDirs( datafile_ ) ) + datafile_ = moose::toFilename( datafile_ ); - outfileIsSet_ = true; setUseStreamer( true ); - // If possible get the format of file as well. - format_ = moose::getExtension( outfile_, true ); + format_ = moose::getExtension( datafile_, true ); if( format_.size() == 0 ) format_ = "csv"; } -string Table::getOutfile( void ) const +string Table::getDatafile( void ) const { - return outfile_; + return datafile_; } // Get the dt_ of this table diff --git a/builtins/Table.h b/builtins/Table.h index 22b55a3f54..ff1edbb3fd 100644 --- a/builtins/Table.h +++ b/builtins/Table.h @@ -10,11 +10,7 @@ #ifndef _TABLE_H #define _TABLE_H - -#if USE_BOOST_FILESYSTEM -#include -#endif /* ----- USE_BOOST_FILESYSTEM ----- */ -#include +using namespace std; /** * Receives and records inputs. Handles plot and spiking data in batch mode. @@ -46,8 +42,8 @@ class Table: public TableBase void setUseSpikeMode ( bool status ); bool getUseSpikeMode ( void ) const; - void setOutfile ( string outfilepath ); - string getOutfile ( void ) const; + void setDatafile ( string filepath ); + string getDatafile ( void ) const; // Access the dt_ of table. double getDt ( void ) const; @@ -88,7 +84,14 @@ class Table: public TableBase vector data_; vector tvec_; /* time data */ - vector columns_; /* Store the name of tables */ + + // A table have 2 columns. First is time. We initialize this in reinit(). + vector columns_; + + /** + * @brief dt of its clock. Needed for creating time co-ordinates, + */ + double dt_; // Upto which indices we have read the data. This variable is used when // SocketStreamer is used. @@ -102,46 +105,21 @@ class Table: public TableBase string tableColumnName_; /** - * @brief If stream is set to true, then stream to outfile_. Default value - * of outfile_ is table path starting from `pwd`/_tables_ . On table, set + * @brief If stream is set to true, then stream to datafile_. Default value + * of datafile_ is table path starting from `pwd`/_tables_ . On table, set * streamToFile to true. */ bool useFileStreamer_; - /** - * @brief Table directory into which dump the stream data. - */ - string rootdir_; - - // On Table, set outfile to change this variable. By default it sets to, + // On Table, set datafile to change this variable. By default it sets to, // `pwd1/_tables_/table.path(). - string outfile_; - - /** - * @brief Wheather or not outfile path is set by user - */ - bool outfileIsSet_; + string datafile_; /** * @brief format of data. Default to csv. */ string format_; - /** - * @brief text_ to write. - */ - string text_; - - /** - * @brief dt of its clock. Needed for creating time co-ordinates, - */ - double dt_; - - /** - * @brief Output stream. - */ - std::ofstream of_; - }; #endif // _TABLE_H diff --git a/shell/Neutral.cpp b/shell/Neutral.cpp index 3e7d448cab..4256ae2187 100644 --- a/shell/Neutral.cpp +++ b/shell/Neutral.cpp @@ -764,7 +764,6 @@ string Neutral::path( const Eref& e ) stringstream ss; pathVec.push_back( curr ); - while ( curr.id != Id() ) { ObjId mid = curr.eref().element()->findCaller( pafid ); @@ -777,10 +776,8 @@ string Neutral::path( const Eref& e ) curr = Msg::getMsg( mid )->findOtherEnd( curr ); pathVec.push_back( curr ); } - if ( pathVec.size() <= 1 ) return "/"; - for ( unsigned int i = 1; i < pathVec.size(); ++i ) { ss << "/"; @@ -795,7 +792,6 @@ string Neutral::path( const Eref& e ) ss << "[" << oid.dataIndex << "]"; */ } - // Append braces if Eref was for a fieldElement. This should // work even if it is off-node. if ( e.element()->hasFields() ) diff --git a/tests/py_moose/test_table_streaming_support.py b/tests/py_moose/test_table_streaming_support.py index fa3809da8f..56abcd865d 100644 --- a/tests/py_moose/test_table_streaming_support.py +++ b/tests/py_moose/test_table_streaming_support.py @@ -22,7 +22,7 @@ def print_table( table ): msg = "" - msg += " outfile : %s" % table.outfile + msg += " datafile : %s" % table.datafile msg += " useStreamer: %s" % table.useStreamer msg += ' Path: %s' % table.path print( msg ) @@ -46,11 +46,12 @@ def test_small( ): tabA.format = 'npy' tabA.useStreamer = 1 # Setting format alone is not good enough + # Setting datafile enables streamer. tabB = moose.Table2( '/compt/b/tabB' ) - tabB.outfile = 'table2.npy' + tabB.datafile = 'table2.npy' tabC = moose.Table2( '/compt/c/tabC' ) - tabC.outfile = 'tablec.csv' + tabC.datafile = 'tablec.csv' moose.connect( tabA, 'requestOut', a, 'getConc' ) moose.connect( tabB, 'requestOut', b, 'getConc' ) @@ -64,7 +65,7 @@ def test_small( ): print( ' MOOSE is done' ) # Now read the numpy and csv and check the results. - a = np.loadtxt( '_tables/compt/a/tabA.csv', skiprows=1 ) + a = np.loadtxt( tabA.datafile, skiprows=1 ) b = np.load( 'table2.npy' ) c = np.loadtxt( 'tablec.csv', skiprows=1 ) assert (len(a) == len(b) == len(c)) @@ -99,25 +100,25 @@ def buildLargeSystem(useStreamer = False): if useStreamer: s = moose.Streamer('/comptB/streamer') - s.outfile = 'data2.npy' + s.datafile = 'data2.npy' print("[INFO ] Total tables %d" % len(tables)) # Add tables using wilcardFind. - s.addTables(moose.wildcardFind('/##[TYPE=Table2]')) + s.addTables(moose.wildcardFind('/comptB/##[TYPE=Table2]')) print("Streamer has %d table" % s.numTables) - assert s.numTables == len(tables) + assert s.numTables == len(tables), (s.numTables, len(tables)) moose.reinit() moose.start(10) if useStreamer: # load the data - data = np.load(s.outfile) + data = np.load(s.datafile) header = str(data.dtype.names) assert len(header) > 2**16 else: - data = { x.path : x.vector for x in tables } + data = { x.columnName : x.vector for x in tables } return data def test_large_system(): @@ -132,7 +133,7 @@ def test_large_system(): # same column names. xNames = list(X.keys()) yNames = list(Y.dtype.names) - assert set(yNames) - set(xNames) == set(['time']) + assert set(yNames) - set(xNames) == set(['time']), (yNames, xNames) # Test for equality in some tables. for i in range(1, 10): @@ -142,7 +143,7 @@ def test_large_system(): def main( ): - # test_small( ) + test_small( ) test_large_system() print( '[INFO] All tests passed' ) From f25469a8b65b2f5aeff0c37aa21c4bb00f4f1a5a Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Tue, 10 Mar 2020 14:33:21 +0530 Subject: [PATCH 03/16] Default tick for Streamer is 10s. --- scheduling/Clock.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scheduling/Clock.cpp b/scheduling/Clock.cpp index e905d705c7..0434c4328b 100644 --- a/scheduling/Clock.cpp +++ b/scheduling/Clock.cpp @@ -1027,8 +1027,8 @@ void Clock::buildDefaultTick() defaultDt_[16] = 0.1; defaultDt_[17] = 0.1; defaultDt_[18] = 1; // For tables for chemical calculations. - defaultDt_[19] = 1; // For Socket Streamer - defaultDt_[20] = 5; // For CSV Streamer + defaultDt_[19] = 1; // For Socket Streamer + defaultDt_[20] = 10; // For CSV Streamer // 20-29 are not assigned. defaultDt_[30] = 1; // For the HDF writer From a10abc99bd8e1b5356fb56aef41f5abb786a9cc1 Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Tue, 10 Mar 2020 17:39:58 +0530 Subject: [PATCH 04/16] Able to run nml2 example with python3 (BhallaLab/moose#253). --- python/moose/neuroml2/reader.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/python/moose/neuroml2/reader.py b/python/moose/neuroml2/reader.py index d858872c73..8140e8c536 100644 --- a/python/moose/neuroml2/reader.py +++ b/python/moose/neuroml2/reader.py @@ -4,25 +4,21 @@ # Description: NeuroML2 reader. # Implementation of reader for NeuroML 2 models. # TODO: handle morphologies of more than one segment... +# # Author: Subhasis Ray, Padraig Gleeson -# Maintainer: Dilawar Singh +# Maintainer: Dilawar Singh # Created: Wed Jul 24 15:55:54 2013 (+0530) +# # Notes: # For update/log, please see git-blame documentation or browse the github # repo https://github.com/BhallaLab/moose-core - -try: - from future_builtins import zip, map -except ImportError as e: - pass - import os import math import numpy as np import moose -import logging +import logging logger_ = logging.getLogger('moose.nml2') import neuroml as nml @@ -549,7 +545,10 @@ def copyChannel(self, chdens, comp, condDensity, erev): orig = chdens.id chid = moose.copy(proto_chan, comp, chdens.id) chan = moose.element(chid) - for p in self.paths_to_chan_elements.keys(): + + # We are updaing the keys() here. Need copy: using list(keys()) to + # create a copy. + for p in list(self.paths_to_chan_elements.keys()): pp = p.replace('%s/'%chdens.ion_channel,'%s/'%orig) self.paths_to_chan_elements[pp] = self.paths_to_chan_elements[p].replace('%s/'%chdens.ion_channel,'%s/'%orig) #logger_.info(self.paths_to_chan_elements) From 6fce93b2af0685b1ac3dbca91bcc8b2a70c277d2 Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Tue, 10 Mar 2020 17:39:58 +0530 Subject: [PATCH 05/16] Able to run nml2 example with python3 (BhallaLab/moose#253). --- python/moose/neuroml2/reader.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/python/moose/neuroml2/reader.py b/python/moose/neuroml2/reader.py index d858872c73..8140e8c536 100644 --- a/python/moose/neuroml2/reader.py +++ b/python/moose/neuroml2/reader.py @@ -4,25 +4,21 @@ # Description: NeuroML2 reader. # Implementation of reader for NeuroML 2 models. # TODO: handle morphologies of more than one segment... +# # Author: Subhasis Ray, Padraig Gleeson -# Maintainer: Dilawar Singh +# Maintainer: Dilawar Singh # Created: Wed Jul 24 15:55:54 2013 (+0530) +# # Notes: # For update/log, please see git-blame documentation or browse the github # repo https://github.com/BhallaLab/moose-core - -try: - from future_builtins import zip, map -except ImportError as e: - pass - import os import math import numpy as np import moose -import logging +import logging logger_ = logging.getLogger('moose.nml2') import neuroml as nml @@ -549,7 +545,10 @@ def copyChannel(self, chdens, comp, condDensity, erev): orig = chdens.id chid = moose.copy(proto_chan, comp, chdens.id) chan = moose.element(chid) - for p in self.paths_to_chan_elements.keys(): + + # We are updaing the keys() here. Need copy: using list(keys()) to + # create a copy. + for p in list(self.paths_to_chan_elements.keys()): pp = p.replace('%s/'%chdens.ion_channel,'%s/'%orig) self.paths_to_chan_elements[pp] = self.paths_to_chan_elements[p].replace('%s/'%chdens.ion_channel,'%s/'%orig) #logger_.info(self.paths_to_chan_elements) From d1b066257536a9a05ab39505d892f5d02d4eab7a Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Tue, 10 Mar 2020 17:54:02 +0530 Subject: [PATCH 06/16] moose.server uses the standard logger and does not set global logging to DEBUG mode. --- python/moose/server.py | 56 +++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/python/moose/server.py b/python/moose/server.py index 7f81d2e796..8252e1928f 100644 --- a/python/moose/server.py +++ b/python/moose/server.py @@ -25,23 +25,11 @@ import tarfile import tempfile import threading -import logging import subprocess # create a logger for this server. -logging.basicConfig( - level=logging.DEBUG, - format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', - datefmt='%m-%d %H:%M', - filename='moose_server.log', - filemode='a' - ) -console = logging.StreamHandler() -console.setLevel(logging.DEBUG) -formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') -console.setFormatter(formatter) -_logger = logging.getLogger('') -_logger.addHandler(console) +import logging +logger_ = logging.getLogger('moose.server') __all__ = [ 'serve' ] @@ -133,7 +121,7 @@ def prefix_data_with_size(data): def signal_handler(signum, frame): global stop_all_ global sock_ - _logger.info( "User terminated all processes." ) + logger_.info( "User terminated all processes." ) stop_all_ = True # sock_.shutdown( socket.SHUT_RDWR ) sock_.close() @@ -149,14 +137,14 @@ def send_msg(msg, conn, prefix='LOG'): if not msg.strip(): return False if prefix != 'TAB': - _logger.debug(msg) + logger_.debug(msg) else: - _logger.debug( 'Sending msg with size %d' % len(msg)) + logger_.debug( 'Sending msg with size %d' % len(msg)) msg = '<%s>%s' % (prefix, msg) conn.sendall(prefix_data_with_size(msg)) def run(cmd, conn, cwd=None): - _logger.info( "Executing %s" % cmd ) + logger_.info( "Executing %s" % cmd ) oldCWD = os.getcwd() if cwd is not None: os.chdir(cwd) @@ -176,7 +164,7 @@ def recv_input(conn, size=1024): try: d = conn.recv(prefixL_, socket.MSG_WAITALL) except Exception: - _logger.error("MSG FORMAT: %d bytes are size of msg."%prefixL_) + logger_.error("MSG FORMAT: %d bytes are size of msg."%prefixL_) continue d, data = int(d), b'' while len(data) < d: @@ -186,12 +174,12 @@ def recv_input(conn, size=1024): def writeTarfile( data ): tfile = os.path.join(tempfile.mkdtemp(), 'data.tar.bz2') with open(tfile, 'wb' ) as f: - _logger.info( "Writing %d bytes to %s" % (len(data), tfile)) + logger_.info( "Writing %d bytes to %s" % (len(data), tfile)) f.write(data) # Sleep for some time so that file can be written to disk. time.sleep(0.1) if not tarfile.is_tarfile(tfile): - _logger.warn( 'Not a valid tar file: %s' % tfile) + logger_.warn( 'Not a valid tar file: %s' % tfile) return None return tfile @@ -210,7 +198,7 @@ def streamer_client(socketPath, conn): # Connect to running socket server. global stop_streamer_ stop = False - _logger.debug( "Trying to connect to server at : %s" % socketPath ) + logger_.debug( "Trying to connect to server at : %s" % socketPath ) while not os.path.exists( socketPath ): #print( 'socket %s is not available yet.' % socketPath ) time.sleep(0.1) @@ -222,12 +210,12 @@ def streamer_client(socketPath, conn): try: stClient.connect(socketPath) except socket.error as e: - _logger.warning('Could not connect: %s' % e) + logger_.warning('Could not connect: %s' % e) return # send streaming data back to client. The streamer send fixed size messages # of 1024/2048 bytes each (see the c++ implmenetation). - _logger.info( "Socket Streamer is connected with server." ) + logger_.info( "Socket Streamer is connected with server." ) stClient.settimeout(0.05) send_msg( b'Now streaming table data.', conn, 'TAB') while not stop: @@ -258,7 +246,7 @@ def run_file(filename, conn, cwd=None): stop_streamer_[streamerThread.name] = True streamerThread.join( timeout = 1) if streamerThread.is_alive(): - _logger.error( "The socket streamer client is still running...") + logger_.error( "The socket streamer client is still running...") def extract_files(tfile, to): userFiles = [] @@ -267,11 +255,11 @@ def extract_files(tfile, to): try: f.extractall( to ) except Exception as e: - _logger.warn( e) + logger_.warn( e) # now check if all files have been extracted properly for f in userFiles: if not os.path.exists(f): - _logger.error( "File %s could not be extracted." % f ) + logger_.error( "File %s could not be extracted." % f ) return userFiles def prepareMatplotlib( cwd ): @@ -288,14 +276,14 @@ def sendResults(tdir, conn, notTheseFiles): resfile = os.path.join(resdir, 'results.tar.bz2') with tarfile.open( resfile, 'w|bz2') as tf: for f in find_files(tdir, ext='png'): - _logger.info( "Adding file %s" % f ) + logger_.info( "Adding file %s" % f ) tf.add(f, os.path.basename(f)) time.sleep(0.01) # now send the tar file back to client with open(resfile, 'rb' ) as f: data = f.read() - _logger.info( 'Total bytes to send to client: %d' % len(data)) + logger_.info( 'Total bytes to send to client: %d' % len(data)) send_bz2(conn, data) shutil.rmtree(resdir) @@ -348,11 +336,11 @@ def savePayload( conn ): def handle_client(conn, ip, port): isActive = True - _logger.info( "Serving request from %s:%s" % (ip, port) ) + logger_.info( "Serving request from %s:%s" % (ip, port) ) while isActive: tarfileName, nBytes = savePayload(conn) if tarfileName is None: - _logger.warn( "Could not recieve data." ) + logger_.warn( "Could not recieve data." ) isActive = False if not os.path.isfile(tarfileName): send_msg("[ERROR] %s is not a valid tarfile. Retry"%tarfileName, conn) @@ -379,9 +367,9 @@ def start_server( host, port, max_requests = 10 ): sock_.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: sock_.bind( (host, port)) - _logger.info( "Server created %s:%s" %(host,port) ) + logger_.info( "Server created %s:%s" %(host,port) ) except Exception as e: - _logger.error( "Failed to bind: %s" % e) + logger_.error( "Failed to bind: %s" % e) quit(1) # listen upto 10 of requests @@ -392,7 +380,7 @@ def start_server( host, port, max_requests = 10 ): sock_.settimeout(10) try: conn, (ip, port) = sock_.accept() - except socket.timeout as e: + except socket.timeout: continue sock_.settimeout(0.0) t = threading.Thread(target=handle_client, args=(conn, ip, port)) From fd77d8e9bd7266357cd4a7b61292e23be988650d Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Tue, 10 Mar 2020 19:06:27 +0530 Subject: [PATCH 07/16] Got reader.py from https://raw.githubusercontent.com/pgleeson/moose-core/9834e98744610d9a920d1b63abfe128794abf16f/python/moose/neuroml2/reader.py --- python/moose/neuroml2/reader.py | 567 +++++++++++++++----------------- 1 file changed, 269 insertions(+), 298 deletions(-) diff --git a/python/moose/neuroml2/reader.py b/python/moose/neuroml2/reader.py index 8140e8c536..f8746fd4c8 100644 --- a/python/moose/neuroml2/reader.py +++ b/python/moose/neuroml2/reader.py @@ -1,125 +1,122 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, division, absolute_import - -# Description: NeuroML2 reader. -# Implementation of reader for NeuroML 2 models. -# TODO: handle morphologies of more than one segment... -# +# reader.py --- +# +# Filename: reader.py +# Description: # Author: Subhasis Ray, Padraig Gleeson -# Maintainer: Dilawar Singh +# Maintainer: # Created: Wed Jul 24 15:55:54 2013 (+0530) +# Version: +# Last-Updated: 15 Jan 2018 +# By: pgleeson +# Update #: -- +# URL: +# Keywords: +# Compatibility: +# # -# Notes: -# For update/log, please see git-blame documentation or browse the github -# repo https://github.com/BhallaLab/moose-core -import os -import math -import numpy as np -import moose +# Commentary: +# +# +# +# -import logging -logger_ = logging.getLogger('moose.nml2') +# Change log: +# +# +# +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, Fifth +# Floor, Boston, MA 02110-1301, USA. +# +# + +# Code: +"""Implementation of reader for NeuroML 2 models. + + +TODO: handle morphologies of more than one segment... + +""" -import neuroml as nml -import pyneuroml.pynml as pynml +from __future__ import print_function +try: + from future_builtins import zip, map +except ImportError: + pass +import sys, os +import numpy as np +from moose.neuroml2.hhfit import exponential2 +from moose.neuroml2.hhfit import sigmoid2 +from moose.neuroml2.hhfit import linoid2 from moose.neuroml2.units import SI +import moose +import logging +import math -def _write_flattened_nml( doc, outfile ): - """_write_flattened_nml - Concat all NML2 read by moose and generate one flattened NML file. - Only useful when debugging. +loglevel = logging.DEBUG +logstream = logging.StreamHandler() +logstream.setLevel(loglevel) +logstream.setFormatter(logging.Formatter('%s(asctime)s %(name)s %(filename)s %(funcName)s: %(message)s')) +logger = logging.getLogger('nml2_reader') +logger.addHandler(logstream) + +try: + import neuroml as nml + import neuroml.loaders as loaders + from pyneuroml import pynml +except: + print("********************************************************************") + print("* ") + print("* Please install libNeuroML & pyNeuroML: ") + print("* pip install libneuroml") + print("* pip install pyNeuroML") + print("* ") + print("* Requirement for this is lxml: ") + print("* apt-get install python-lxml") + print("* ") + print("********************************************************************") + +# Utility functions - :param doc: NML document (nml.doc) - :param outfile: Name of the output file. +def sarea(comp): """ - import neuroml.writers - neuroml.writers.NeuroMLWriter.write( doc, outfile ) - logger_.debug( "Wrote flattened NML model to %s" % outfile ) - -def _gates_sorted( all_gates ): - """_gates_sorted + Return the surface area of compartment from length and + diameter. Parameters ---------- - all_gates (list) - List of all moose.HHChannel.gates - - Notes - ----- - If the id of gates are subset of 'x', 'y' or 'z' then sort them so they load in - X, Y or Z gate respectively. Otherwise do not touch them i.e. first gate - will be loaded into X, second into Y and so on. - """ - allMooseGates = ['x', 'y', 'z'] - allGatesDict = { g.id : g for g in all_gates } - gateNames = [ g.id.lower() for g in all_gates ] - if set(gateNames).issubset(set(allMooseGates)): - sortedGates = [] - for gid in allMooseGates: - sortedGates.append( allGatesDict.get(gid) ) - return sortedGates - return all_gates - -def _unique( ls ): - res = [ ] - for l in ls: - if l not in res: - res.append( l ) - return res - -def _isConcDep(ct): - """_isConcDep - Check if componet is dependant on concentration. Most HHGates are - dependant on voltage. - - :param ct: ComponentType - :type ct: nml.ComponentType - - :return: True if Component is depenant on conc, False otherwise. - """ - if 'ConcDep' in ct.extends: - return True - return False - -def _findCaConcVariableName(): - """_findCaConcVariableName - Find a suitable CaConc for computing HHGate tables. - This is a hack, though it is likely to work in most cases. - """ - caConcs = moose.wildcardFind( '/library/##[TYPE=CaConc]' ) - assert len(caConcs) >= 1, "No moose.CaConc found. Currently moose \ - supports HHChannel which depends only on moose.CaConc ." - return caConcs[0].name + comp : Compartment instance. -def sarea(comp): - """sarea - Return the surface area (2ϖrL) of compartment from length and diameter. + Returns + ------- + s : float + surface area of `comp`. - :param comp: Compartment instance. - :type comp: str - :return: surface area of `comp`. - :rtype: float """ if comp.length > 0: - return math.pi * comp.diameter * comp.length + return comp.length * comp.diameter * np.pi else: - return math.pi * comp.diameter * comp.diameter - -def xarea(compt): - """xarea - Return the cross sectional area (𝜋r²) from the diameter of the compartment. + return comp.diameter * comp.diameter * np.pi - Note: - ---- - How to do it for spherical compartment? - - :param compt: Compartment in moose. - :type compt: moose.Compartment - :return: cross sectional area. - :rtype: float +def xarea(comp): """ - return math.pi * (compt.diameter/2.0)**2.0 + Return the cross sectional area from diameter of the + compartment. How to do it for spherical compartment?""" + return comp.diameter * comp.diameter * np.pi / 4.0 def setRa(comp, resistivity): """Calculate total raxial from specific value `resistivity`""" @@ -136,16 +133,25 @@ def setEk(comp, erev): """Set reversal potential""" comp.setEm(erev) + def getSegments(nmlcell, component, sg_to_segments): """Get the list of segments the `component` is applied to""" sg = component.segment_groups + #seg = component.segment if sg is None: - segments = nmlcell.morphology.segments - elif sg == 'all': + segments = nmlcell.morphology.segments + elif sg == 'all': # Special case segments = [seg for seglist in sg_to_segments.values() for seg in seglist] else: segments = sg_to_segments[sg] - return _unique(segments) + + unique_segs = [] + unique_segs_ids = [] + for s in segments: + if not s.id in unique_segs_ids: + unique_segs.append(s) + unique_segs_ids.append(s.id) + return unique_segs class NML2Reader(object): @@ -156,88 +162,78 @@ class NML2Reader(object): Example: - >>> import moose - >>> reader = moose.NML2Reader() + >>> from moose import neuroml2 as nml + >>> reader = nml.NML2Reader() >>> reader.read('moose/neuroml2/test_files/Purk2M9s.nml') creates a passive neuronal morphology `/library/Purk2M9s`. """ def __init__(self, verbose=False): - global logger_ self.lunit = 1e-6 # micron is the default length unit self.verbose = verbose - if self.verbose: - logger_.setLevel( logging.DEBUG ) self.doc = None self.filename = None - self.nml_to_moose = {} # NeuroML object to MOOSE object + self.nml_cells_to_moose = {} # NeuroML object to MOOSE object + self.nml_segs_to_moose = {} # NeuroML object to MOOSE object + self.nml_chans_to_moose = {} # NeuroML object to MOOSE object + self.nml_conc_to_moose = {} # NeuroML object to MOOSE object self.moose_to_nml = {} # Moose object to NeuroML object - self.proto_cells = {} # map id to prototype cell in moose - self.proto_chans = {} # map id to prototype channels in moose - self.proto_pools = {} # map id to prototype pools (Ca2+, Mg2+) - self.includes = {} # Included files mapped to other readers - self.lib = moose.element('/library') if moose.exists('/library') \ - else moose.Neutral( '/library' ) + self.proto_cells = {} # map id to prototype cell in moose + self.proto_chans = {} # map id to prototype channels in moose + self.proto_pools = {} # map id to prototype pools (Ca2+, Mg2+) + self.includes = {} # Included files mapped to other readers + self.lib = moose.Neutral('/library') self.id_to_ionChannel = {} self._cell_to_sg = {} # nml cell to dict - the dict maps segment groups to segments - self._variables = {} self.cells_in_populations = {} self.pop_to_cell_type = {} self.seg_id_to_comp_name = {} self.paths_to_chan_elements = {} - self.network = None def read(self, filename, symmetric=True): - filename = os.path.realpath( filename ) - self.doc = nml.loaders.read_neuroml2_file( - filename, include_includes=True, verbose=self.verbose) + self.doc = loaders.read_neuroml2_file(filename, include_includes=True, verbose=self.verbose) - self.filename = filename - - logger_.info('Parsed NeuroML2 file: %s'% filename) if self.verbose: - _write_flattened_nml( self.doc, '%s__flattened.xml' % self.filename ) + print('Parsed NeuroML2 file: %s'% filename) + self.filename = filename if len(self.doc.networks)>=1: self.network = self.doc.networks[0] + moose.celsius = self._getTemperature() self.importConcentrationModels(self.doc) self.importIonChannels(self.doc) self.importInputs(self.doc) + for cell in self.doc.cells: self.createCellPrototype(cell, symmetric=symmetric) if len(self.doc.networks)>=1: self.createPopulations() self.createInputs() + print("Read all from %s"%filename) def _getTemperature(self): - if self.network is not None: - if self.network.type=="networkWithTemperature": - return SI(self.network.temperature) - else: - # Why not, if there's no temp dependence in nml..? - return 0 - return SI('25') + if self.network.type=="networkWithTemperature": + return SI(self.network.temperature) + else: + return 0 # Why not, if there's no temp dependence in nml..? def getCellInPopulation(self, pop_id, index): return self.cells_in_populations[pop_id][index] def getComp(self, pop_id, cellIndex, segId): - return moose.element('%s/%s/%s/%s' % ( self.lib.path, pop_id, cellIndex - , self.seg_id_to_comp_name[self.pop_to_cell_type[pop_id]][segId]) - ) + return moose.element('%s/%s/%s/%s' % (self.lib.path, pop_id, cellIndex, self.seg_id_to_comp_name[self.pop_to_cell_type[pop_id]][segId])) def createPopulations(self): for pop in self.network.populations: - ep = '%s/%s' % (self.lib.path, pop.id) - mpop = moose.element(ep) if moose.exists(ep) else moose.Neutral(ep) + mpop = moose.Neutral('%s/%s' % (self.lib.path, pop.id)) self.cells_in_populations[pop.id] ={} for i in range(pop.size): - logger_.info("Creating %s/%s instances of %s under %s"%(i,pop.size,pop.component, mpop)) + print("Creating %s/%s instances of %s under %s"%(i,pop.size,pop.component, mpop)) self.pop_to_cell_type[pop.id]=pop.component chid = moose.copy(self.proto_cells[pop.component], mpop, '%s'%(i)) self.cells_in_populations[pop.id][i]=chid @@ -260,17 +256,14 @@ def createInputs(self): for il in self.network.input_lists: for ii in il.input: input = self.getInput(il.component) - moose.connect(input, 'output' - , self.getComp(il.populations,ii.get_target_cell__hash(),ii.get_segment__hash()) - , 'injectMsg') + moose.connect(input, 'output', self.getComp(il.populations,ii.get_target_cell_id(),ii.get_segment_id()), 'injectMsg') def createCellPrototype(self, cell, symmetric=True): """To be completed - create the morphology, channels in prototype""" - ep = '%s/%s' % (self.lib.path, cell.id) - nrn = moose.element(ep) if moose.exists(ep) else moose.Neuron(ep) + nrn = moose.Neuron('%s/%s' % (self.lib.path, cell.id)) self.proto_cells[cell.id] = nrn - self.nml_to_moose[cell.id] = nrn + self.nml_cells_to_moose[cell.id] = nrn self.moose_to_nml[nrn] = cell self.createMorphology(cell, nrn, symmetric=symmetric) self.importBiophysics(cell, nrn) @@ -297,13 +290,11 @@ def createMorphology(self, nmlcell, moosecell, symmetric=True): self.seg_id_to_comp_name[nmlcell.id]={} for seg in segments: if seg.name is not None: - ep = '%s/%s' % (cellpath, seg.name) - id_to_comp[seg.id] = moose.element(ep) if moose.exists(ep) else compclass(ep) + id_to_comp[seg.id] = compclass('%s/%s' % (cellpath, seg.name)) self.seg_id_to_comp_name[nmlcell.id][seg.id] = seg.name else: name = 'comp_%s'%seg.id - ep = '%s/%s' % (cellpath, name) - id_to_comp[seg.id] = moose.element(ep) if moose.exists(ep) else compclass(ep) + id_to_comp[seg.id] = compclass('%s/%s' % (cellpath, name)) self.seg_id_to_comp_name[nmlcell.id][seg.id] = name # Now assign the positions and connect up axial resistance if not symmetric: @@ -317,21 +308,19 @@ def createMorphology(self, nmlcell, moosecell, symmetric=True): except AttributeError: parent = None self.moose_to_nml[comp] = segment - self.nml_to_moose[segment.id] = comp + self.nml_segs_to_moose[segment.id] = comp p0 = segment.proximal if p0 is None: if parent: p0 = parent.distal else: - raise Exception( - 'No proximal point and no parent segment for segment:'+\ - 'name=%s, id=%s' % (segment.name, segment.id) - ) - comp.x0, comp.y0, comp.z0 = (x*self.lunit for x in map(float, (p0.x, p0.y, p0.z))) + raise Exception('No proximal point and no parent segment for segment: name=%s, id=%s' % (segment.name, segment.id)) + comp.x0, comp.y0, comp.z0 = (x * self.lunit for x in map(float, (p0.x, p0.y, p0.z))) p1 = segment.distal - comp.x, comp.y, comp.z = (x*self.lunit for x in map(float, (p1.x, p1.y, p1.z))) - comp.length = np.sqrt((comp.x-comp.x0)**2+(comp.y-comp.y0)**2+(comp.z-comp.z0)**2) - + comp.x, comp.y, comp.z = (x * self.lunit for x in map(float, (p1.x, p1.y, p1.z))) + comp.length = np.sqrt((comp.x - comp.x0)**2 + + (comp.y - comp.y0)**2 + + (comp.z - comp.z0)**2) # This can pose problem with moose where both ends of # compartment have same diameter. We are averaging the two # - may be splitting the compartment into two is better? @@ -360,7 +349,7 @@ def importBiophysics(self, nmlcell, moosecell): according to NeuroML2 cell `nmlcell`.""" bp = nmlcell.biophysical_properties if bp is None: - logger_.info('Warning: %s in %s has no biophysical properties' % (nmlcell.id, self.filename)) + print('Warning: %s in %s has no biophysical properties' % (nmlcell.id, self.filename)) return self.importMembraneProperties(nmlcell, moosecell, bp.membrane_properties) self.importIntracellularProperties(nmlcell, moosecell, bp.intracellular_properties) @@ -368,7 +357,7 @@ def importBiophysics(self, nmlcell, moosecell): def importMembraneProperties(self, nmlcell, moosecell, mp): """Create the membrane properties from nmlcell in moosecell""" if self.verbose: - logger_.info('Importing membrane properties') + print('Importing membrane properties') self.importCapacitances(nmlcell, moosecell, mp.specific_capacitances) self.importChannelsToCell(nmlcell, moosecell, mp) self.importInitMembPotential(nmlcell, moosecell, mp) @@ -378,7 +367,7 @@ def importCapacitances(self, nmlcell, moosecell, specificCapacitances): for specific_cm in specificCapacitances: cm = SI(specific_cm.value) for seg in sg_to_segments[specific_cm.segment_groups]: - comp = self.nml_to_moose[seg.id] + comp = self.nml_segs_to_moose[seg.id] comp.Cm = sarea(comp) * cm def importInitMembPotential(self, nmlcell, moosecell, membraneProperties): @@ -386,7 +375,7 @@ def importInitMembPotential(self, nmlcell, moosecell, membraneProperties): for imp in membraneProperties.init_memb_potentials: initv = SI(imp.value) for seg in sg_to_segments[imp.segment_groups]: - comp = self.nml_to_moose[seg.id] + comp = self.nml_segs_to_moose[seg.id] comp.initVm = initv def importIntracellularProperties(self, nmlcell, moosecell, properties): @@ -396,40 +385,30 @@ def importIntracellularProperties(self, nmlcell, moosecell, properties): def importSpecies(self, nmlcell, properties): sg_to_segments = self._cell_to_sg[nmlcell.id] for species in properties.species: - # Developer note: Not sure if species.concentration_model should be - # a nml element of just plain string. I was getting plain text from - # nml file here. - concModel = species.concentration_model - if (concModel is not None) and (concModel not in self.proto_pools): - logger_.warn("No concentrationModel '%s' found."%concModel) + if (species.concentration_model is not None) and \ + (species.concentration_model.id not in self.proto_pools): continue segments = getSegments(nmlcell, species, sg_to_segments) for seg in segments: - comp = self.nml_to_moose[seg.id] + comp = self.nml_segs_to_moose[seg.id] self.copySpecies(species, comp) def copySpecies(self, species, compartment): """Copy the prototype pool `species` to compartment. Currently only decaying pool of Ca2+ supported""" proto_pool = None - concModel = species.concentration_model - if concModel in self.proto_pools: - proto_pool = self.proto_pools[concModel] + if species.concentrationModel in self.proto_pools: + proto_pool = self.proto_pools[species.concentration_model] else: for innerReader in self.includes.values(): - if concModel in innerReader.proto_pools: - proto_pool = innerReader.proto_pools[concModel] + if species.concentrationModel in innerReader.proto_pools: + proto_pool = innerReader.proto_pools[species.concentrationModel] break if not proto_pool: - msg = 'No prototype pool for %s referred to by %s' % (concModel, species.id) - logger_.error(msg) - raise RuntimeError(msg) - pool_id = moose.copy(proto_pool, compartment, species.id) + raise Exception('No prototype pool for %s referred to by %s' % (species.concentration_model, species.id)) + pool_id = moose.copy(proto_pool, comp, species.id) pool = moose.element(pool_id) - pool.B = pool.B / (np.pi * compartment.length * ( - 0.5 * compartment.diameter + pool.thick) * - (0.5 * compartment.diameter - pool.thick) - ) + pool.B = pool.B / (np.pi * compartment.length * (0.5 * compartment.diameter + pool.thickness) * (0.5 * compartment.diameter - pool.thickness)) return pool def importAxialResistance(self, nmlcell, intracellularProperties): @@ -437,7 +416,7 @@ def importAxialResistance(self, nmlcell, intracellularProperties): for r in intracellularProperties.resistivities: segments = getSegments(nmlcell, r, sg_to_segments) for seg in segments: - comp = self.nml_to_moose[seg.id] + comp = self.nml_segs_to_moose[seg.id] setRa(comp, SI(r.value)) def isPassiveChan(self,chan): @@ -446,55 +425,35 @@ def isPassiveChan(self,chan): if hasattr(chan,'gates'): return len(chan.gate_hh_rates)+len(chan.gates)==0 return False - - def evaluate_moose_component(self, ct, variables): - print( "[INFO ] Not implemented." ) - return False - def calculateRateFn(self, ratefn, vmin, vmax, tablen=3000, vShift='0mV'): - """Returns A / B table from ngate.""" - from . import hhfit - rate_fn_map = { - 'HHExpRate': hhfit.exponential2, - 'HHSigmoidRate': hhfit.sigmoid2, - 'HHSigmoidVariable': hhfit.sigmoid2, - 'HHExpLinearRate': hhfit.linoid2 - } + rate_fn_map = { + 'HHExpRate': exponential2, + 'HHSigmoidRate': sigmoid2, + 'HHSigmoidVariable': sigmoid2, + 'HHExpLinearRate': linoid2 } + def calculateRateFn(self, ratefn, vmin, vmax, tablen=3000, vShift='0mV'): + """Returns A / B table from ngate.""" tab = np.linspace(vmin, vmax, tablen) if self._is_standard_nml_rate(ratefn): midpoint, rate, scale = map(SI, (ratefn.midpoint, ratefn.rate, ratefn.scale)) - return rate_fn_map[ratefn.type](tab, rate, scale, midpoint) - - for ct in self.doc.ComponentType: - if ratefn.type != ct.name: - continue - - logger_.info("Using %s to evaluate rate"%ct.name) - rate = [] - for v in tab: - # Note: MOOSE HHGate are either voltage of concentration - # dependant. Here we figure out if nml description of gate is - # concentration dependant or note. - if _isConcDep(ct): - # Concentration dependant. Concentration can't be negative. - # Find a suitable CaConc from the /library. Currently on Ca - # dependant channels are allowed. - caConcName = _findCaConcVariableName() - req_vars = {caConcName:'%g'%max(0,v),'vShift':vShift,'temperature':self._getTemperature()} - else: - req_vars = {'v':'%sV'%v,'vShift':vShift,'temperature':self._getTemperature()} - req_vars.update( self._variables ) - vals = pynml.evaluate_component(ct, req_variables=req_vars) - v = vals.get('x', vals.get('t', vals.get('r', None))) - if v is not None: - rate.append(v) - return np.array(rate) - - print( "[WARN ] Could not determine rate: %s %s %s" %(ratefn.type,vmin,vmax)) - return np.array([]) - + return self.rate_fn_map[ratefn.type](tab, rate, scale, midpoint) + else: + for ct in self.doc.ComponentType: + if ratefn.type == ct.name: + print("Using %s to evaluate rate"%ct.name) + rate = [] + for v in tab: + vals = pynml.evaluate_component(ct,req_variables={'v':'%sV'%v,'vShift':vShift,'temperature':self._getTemperature()}) + '''print vals''' + if 'x' in vals: + rate.append(vals['x']) + if 't' in vals: + rate.append(vals['t']) + if 'r' in vals: + rate.append(vals['r']) + return np.array(rate) def importChannelsToCell(self, nmlcell, moosecell, membrane_properties): sg_to_segments = self._cell_to_sg[nmlcell.id] @@ -505,24 +464,22 @@ def importChannelsToCell(self, nmlcell, moosecell, membrane_properties): try: ionChannel = self.id_to_ionChannel[chdens.ion_channel] except KeyError: - logger_.info('No channel with id: %s' % chdens.ion_channel) + print('No channel with id', chdens.ion_channel) continue if self.verbose: - logger_.info('Setting density of channel %s in %s to %s; erev=%s (passive: %s)'%( - chdens.id, segments, condDensity,erev,self.isPassiveChan(ionChannel)) - ) + print('Setting density of channel %s in %s to %s; erev=%s (passive: %s)'%(chdens.id, segments, condDensity,erev,self.isPassiveChan(ionChannel))) if self.isPassiveChan(ionChannel): for seg in segments: - # comp = self.nml_to_moose[seg] - setRm(self.nml_to_moose[seg.id], condDensity) - setEk(self.nml_to_moose[seg.id], erev) + comp = self.nml_segs_to_moose[seg.id] + setRm(comp, condDensity) + setEk(comp, erev) else: for seg in segments: - self.copyChannel(chdens, self.nml_to_moose[seg.id], condDensity, erev) - '''moose.le(self.nml_to_moose[seg]) - moose.showfield(self.nml_to_moose[seg], field="*", showtype=True)''' + self.copyChannel(chdens, self.nml_segs_to_moose[seg.id], condDensity, erev) + '''moose.le(self.nml_segs_to_moose[seg.id]) + moose.showfield(self.nml_segs_to_moose[seg.id], field="*", showtype=True)''' def copyChannel(self, chdens, comp, condDensity, erev): """Copy moose prototype for `chdens` condutcance density to `comp` @@ -541,22 +498,46 @@ def copyChannel(self, chdens, comp, condDensity, erev): raise Exception('No prototype channel for %s referred to by %s' % (chdens.ion_channel, chdens.id)) if self.verbose: - logger_.info('Copying %s to %s, %s; erev=%s'%(chdens.id, comp, condDensity, erev)) + print('Copying %s to %s, %s; erev=%s'%(chdens.id, comp, condDensity, erev)) orig = chdens.id chid = moose.copy(proto_chan, comp, chdens.id) chan = moose.element(chid) - - # We are updaing the keys() here. Need copy: using list(keys()) to - # create a copy. - for p in list(self.paths_to_chan_elements.keys()): + els = list(self.paths_to_chan_elements.keys()) + for p in els: pp = p.replace('%s/'%chdens.ion_channel,'%s/'%orig) self.paths_to_chan_elements[pp] = self.paths_to_chan_elements[p].replace('%s/'%chdens.ion_channel,'%s/'%orig) - #logger_.info(self.paths_to_chan_elements) + #print(self.paths_to_chan_elements) chan.Gbar = sarea(comp) * condDensity chan.Ek = erev moose.connect(chan, 'channel', comp, 'channel') return chan + ''' + def importIncludes(self, doc): + for include in doc.include: + if self.verbose: + print(self.filename, 'Loading include', include) + error = None + inner = NML2Reader(self.verbose) + paths = [include.href, os.path.join(os.path.dirname(self.filename), include.href)] + for path in paths: + try: + inner.read(path) + if self.verbose: + print(self.filename, 'Loaded', path, '... OK') + except IOError as e: + error = e + else: + self.includes[include.href] = inner + self.id_to_ionChannel.update(inner.id_to_ionChannel) + self.nml_to_moose.update(inner.nml_to_moose) + self.moose_to_nml.update(inner.moose_to_nml) + error = None + break + if error: + print(self.filename, 'Last exception:', error) + raise IOError('Could not read any of the locations: %s' % (paths))''' + def _is_standard_nml_rate(self, rate): return rate.type=='HHExpLinearRate' \ or rate.type=='HHExpRate' or \ @@ -565,25 +546,19 @@ def _is_standard_nml_rate(self, rate): def createHHChannel(self, chan, vmin=-150e-3, vmax=100e-3, vdivs=5000): mchan = moose.HHChannel('%s/%s' % (self.lib.path, chan.id)) - mgates = [moose.element(x) for x in [mchan.gateX, mchan.gateY, mchan.gateZ]] - assert len(chan.gate_hh_rates)<=3, "We handle only up to 3 gates in HHCHannel" + mgates = map(moose.element, (mchan.gateX, mchan.gateY, mchan.gateZ)) + assert(len(chan.gate_hh_rates) <= 3) # We handle only up to 3 gates in HHCHannel if self.verbose: - logger_.info('== Creating channel: %s (%s) -> %s (%s)'%(chan.id, chan.gate_hh_rates, mchan, mgates)) - + print('== Creating channel: %s (%s) -> %s (%s)'%(chan.id, chan.gate_hh_rates, mchan, mgates)) all_gates = chan.gates + chan.gate_hh_rates - - # Sort all_gates such that they come in x, y, z order. - all_gates = _gates_sorted( all_gates ) - for ngate, mgate in zip(all_gates, mgates): - if ngate is None: - continue + for ngate, mgate in zip(all_gates,mgates): if mgate.name.endswith('X'): mchan.Xpower = ngate.instances elif mgate.name.endswith('Y'): mchan.Ypower = ngate.instances elif mgate.name.endswith('Z'): - mchan.Zpower = ngate.instances + mchan.Zpower = ngate.instance mgate.min = vmin mgate.max = vmax mgate.divs = vdivs @@ -597,31 +572,27 @@ def createHHChannel(self, chan, vmin=-150e-3, vmax=100e-3, vdivs=5000): # refering to tau_inf and m_inf?? fwd = ngate.forward_rate rev = ngate.reverse_rate + self.paths_to_chan_elements['%s/%s'%(chan.id,ngate.id)] = '%s/%s'%(chan.id,mgate.name) + q10_scale = 1 if ngate.q10_settings: if ngate.q10_settings.type == 'q10Fixed': q10_scale= float(ngate.q10_settings.fixed_q10) elif ngate.q10_settings.type == 'q10ExpTemp': - q10_scale = math.pow( float(ngate.q10_settings.q10_factor) - , (self._getTemperature()- SI(ngate.q10_settings.experimental_temp))/10 - ) + q10_scale = math.pow(float(ngate.q10_settings.q10_factor),(self._getTemperature()- SI(ngate.q10_settings.experimental_temp))/10) + #print('Q10: %s; %s; %s; %s'%(ngate.q10_settings.q10_factor, self._getTemperature(),SI(ngate.q10_settings.experimental_temp),q10_scale)) else: - raise Exception('Unknown Q10 scaling type %s: %s'%( - ngate.q10_settings.type,ngate.q10_settings) - ) + raise Exception('Unknown Q10 scaling type %s: %s'%(ngate.q10_settings.type,ngate.q10_settings)) - logger_.debug('+ Gate: %s; %s; %s; %s; %s; scale=%s'% ( - ngate.id, mgate.path, mchan.Xpower, fwd, rev, q10_scale) - ) + if self.verbose: + print(' === Gate: %s; %s; %s; %s; %s; scale=%s'%(ngate.id, mgate.path, mchan.Xpower, fwd, rev, q10_scale)) if (fwd is not None) and (rev is not None): alpha = self.calculateRateFn(fwd, vmin, vmax, vdivs) beta = self.calculateRateFn(rev, vmin, vmax, vdivs) - mgate.tableA = q10_scale * (alpha) mgate.tableB = q10_scale * (alpha + beta) - # Assuming the meaning of the elements in GateHHTauInf ... if hasattr(ngate,'time_course') and hasattr(ngate,'steady_state') \ and (ngate.time_course is not None) and (ngate.steady_state is not None): @@ -635,34 +606,26 @@ def createHHChannel(self, chan, vmin=-150e-3, vmax=100e-3, vdivs=5000): if hasattr(ngate,'steady_state') and (ngate.time_course is None) and (ngate.steady_state is not None): inf = ngate.steady_state tau = 1 / (alpha + beta) - if inf is not None: + if (inf is not None): inf = self.calculateRateFn(inf, vmin, vmax, vdivs) - if len(inf) > 0: - mgate.tableA = q10_scale * (inf / tau) - mgate.tableB = q10_scale * (1 / tau) + mgate.tableA = q10_scale * (inf / tau) + mgate.tableB = q10_scale * (1 / tau) - logger_.info('%s: Created %s for %s'%(self.filename,mchan.path,chan.id)) + if self.verbose: + print(self.filename, '== Created', mchan.path, 'for', chan.id) return mchan def createPassiveChannel(self, chan): - epath = '%s/%s' % (self.lib.path, chan.id) - if moose.exists( epath ): - mchan = moose.element(epath) - else: - mchan = moose.Leakage(epath) - logger_.info('%s: Created %s for %s'%(self.filename,mchan.path,chan.id)) + mchan = moose.Leakage('%s/%s' % (self.lib.path, chan.id)) + if self.verbose: + print(self.filename, 'Created', mchan.path, 'for', chan.id) return mchan def importInputs(self, doc): - epath = '%s/inputs' % (self.lib.path) - if moose.exists( epath ): - minputs = moose.element( epath ) - else: - minputs = moose.Neutral( epath ) - + minputs = moose.Neutral('%s/inputs' % (self.lib.path)) for pg_nml in doc.pulse_generators: - epath = '%s/%s' % (minputs.path, pg_nml.id) - pg = moose.element(epath) if moose.exists(epath) else moose.PulseGen(epath) + + pg = moose.PulseGen('%s/%s' % (minputs.path, pg_nml.id)) pg.firstDelay = SI(pg_nml.delay) pg.firstWidth = SI(pg_nml.duration) pg.firstLevel = SI(pg_nml.amplitude) @@ -670,7 +633,8 @@ def importInputs(self, doc): def importIonChannels(self, doc, vmin=-150e-3, vmax=100e-3, vdivs=5000): - logger_.info('%s: Importing the ion channels' % self.filename ) + if self.verbose: + print(self.filename, 'Importing the ion channels') for chan in doc.ion_channel+doc.ion_channel_hhs: if chan.type == 'ionChannelHH': @@ -681,30 +645,37 @@ def importIonChannels(self, doc, vmin=-150e-3, vmax=100e-3, vdivs=5000): mchan = self.createHHChannel(chan) self.id_to_ionChannel[chan.id] = chan - self.nml_to_moose[chan.id] = mchan + self.nml_chans_to_moose[chan.id] = mchan self.proto_chans[chan.id] = mchan - logger_.info(self.filename + ': Created ion channel %s for %s %s'%( - mchan.path, chan.type, chan.id)) + if self.verbose: + print(self.filename, 'Created ion channel', mchan.path, 'for', chan.type, chan.id) def importConcentrationModels(self, doc): for concModel in doc.decaying_pool_concentration_models: - self.createDecayingPoolConcentrationModel(concModel) + proto = self.createDecayingPoolConcentrationModel(concModel) def createDecayingPoolConcentrationModel(self, concModel): """Create prototype for concentration model""" - if hasattr(concModel, 'name') and concModel.name is not None: + if concModel.name is not None: name = concModel.name else: name = concModel.id - - ca = moose.CaConc('%s/%s' % (self.lib.path, name)) - ca.CaBasal = SI(concModel.resting_conc) - ca.tau = SI(concModel.decay_constant) - ca.thick = SI(concModel.shell_thickness) - ca.B = 5.2e-6 # B = 5.2e-6/(Ad) where A is the area of the - # shell and d is thickness - must divide by - # shell volume when copying + ca = moose.CaConc('%s/%s' % (self.lib.path, id)) + print('11111', concModel.restingConc) + print('2222', concModel.decayConstant) + print('33333', concModel.shellThickness) + + ca.CaBasal = SI(concModel.restingConc) + ca.tau = SI(concModel.decayConstant) + ca.thick = SI(concModel.shellThickness) + ca.B = 5.2e-6 # B = 5.2e-6/(Ad) where A is the area of the shell and d is thickness - must divide by shell volume when copying self.proto_pools[concModel.id] = ca - self.nml_to_moose[name] = ca + self.nml_concs_to_moose[concModel.id] = ca self.moose_to_nml[ca] = concModel - logger_.debug('Created moose element: %s for nml conc %s' % (ca.path, concModel.id)) + logger.debug('Created moose element: %s for nml conc %s' % (ca.path, concModel.id)) + + + + +# +# reader.py ends here From 2143a04821393ede623f858782514375fa569215 Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Tue, 10 Mar 2020 19:22:02 +0530 Subject: [PATCH 08/16] Pulled changes from https://github.com/pgleeson/moose-core/commit/29e089aa950c0cf9d277edaadd77eb9e4d301538 --- python/moose/neuroml2/reader.py | 75 +++++---------------------------- 1 file changed, 10 insertions(+), 65 deletions(-) diff --git a/python/moose/neuroml2/reader.py b/python/moose/neuroml2/reader.py index f8746fd4c8..86b871eb26 100644 --- a/python/moose/neuroml2/reader.py +++ b/python/moose/neuroml2/reader.py @@ -1,48 +1,8 @@ # -*- coding: utf-8 -*- -# reader.py --- -# -# Filename: reader.py -# Description: + # Author: Subhasis Ray, Padraig Gleeson # Maintainer: # Created: Wed Jul 24 15:55:54 2013 (+0530) -# Version: -# Last-Updated: 15 Jan 2018 -# By: pgleeson -# Update #: -- -# URL: -# Keywords: -# Compatibility: -# -# - -# Commentary: -# -# -# -# - -# Change log: -# -# -# -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 3, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 51 Franklin Street, Fifth -# Floor, Boston, MA 02110-1301, USA. -# -# # Code: """Implementation of reader for NeuroML 2 models. @@ -51,13 +11,8 @@ TODO: handle morphologies of more than one segment... """ +from __future__ import print_function, division, absolute_import -from __future__ import print_function -try: - from future_builtins import zip, map -except ImportError: - pass -import sys, os import numpy as np from moose.neuroml2.hhfit import exponential2 from moose.neuroml2.hhfit import sigmoid2 @@ -176,7 +131,7 @@ def __init__(self, verbose=False): self.nml_cells_to_moose = {} # NeuroML object to MOOSE object self.nml_segs_to_moose = {} # NeuroML object to MOOSE object self.nml_chans_to_moose = {} # NeuroML object to MOOSE object - self.nml_conc_to_moose = {} # NeuroML object to MOOSE object + self.nml_concs_to_moose = {} # NeuroML object to MOOSE object self.moose_to_nml = {} # Moose object to NeuroML object self.proto_cells = {} # map id to prototype cell in moose self.proto_chans = {} # map id to prototype channels in moose @@ -656,26 +611,16 @@ def importConcentrationModels(self, doc): def createDecayingPoolConcentrationModel(self, concModel): """Create prototype for concentration model""" - if concModel.name is not None: + name = concModel.id + if hasattr(concModel, 'name') and concModel.name is not None: name = concModel.name - else: - name = concModel.id - ca = moose.CaConc('%s/%s' % (self.lib.path, id)) - print('11111', concModel.restingConc) - print('2222', concModel.decayConstant) - print('33333', concModel.shellThickness) - - ca.CaBasal = SI(concModel.restingConc) - ca.tau = SI(concModel.decayConstant) - ca.thick = SI(concModel.shellThickness) + ca = moose.CaConc('%s/%s' % (self.lib.path, name)) + + ca.CaBasal = SI(concModel.resting_conc) + ca.tau = SI(concModel.decay_constant) + ca.thick = SI(concModel.shell_thickness) ca.B = 5.2e-6 # B = 5.2e-6/(Ad) where A is the area of the shell and d is thickness - must divide by shell volume when copying self.proto_pools[concModel.id] = ca self.nml_concs_to_moose[concModel.id] = ca self.moose_to_nml[ca] = concModel logger.debug('Created moose element: %s for nml conc %s' % (ca.path, concModel.id)) - - - - -# -# reader.py ends here From 74de3b13916b1326bd61cb5eeb0d76d7cd6b89d5 Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Tue, 10 Mar 2020 19:33:36 +0530 Subject: [PATCH 09/16] Fixes after merge. --- python/moose/neuroml2/reader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/moose/neuroml2/reader.py b/python/moose/neuroml2/reader.py index c945cbc49a..9f4cf43fee 100644 --- a/python/moose/neuroml2/reader.py +++ b/python/moose/neuroml2/reader.py @@ -618,4 +618,4 @@ def createDecayingPoolConcentrationModel(self, concModel): self.proto_pools[concModel.id] = ca self.nml_concs_to_moose[concModel.id] = ca self.moose_to_nml[ca] = concModel - logger.debug('Created moose element: %s for nml conc %s' % (ca.path, concModel.id)) + logger_.debug('Created moose element: %s for nml conc %s' % (ca.path, concModel.id)) From 1af4b641a6e13fe3f2aaf2dbc403972839cb5c70 Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Wed, 11 Mar 2020 16:04:54 +0530 Subject: [PATCH 10/16] Some more tweaks to get CaConc working with NML. --- biophysics/CaConc.cpp | 205 +++++++++--------- biophysics/CaConc.h | 72 +++--- biophysics/CaConcBase.cpp | 50 +++-- python/moose/neuroml2/reader.py | 373 +++++++++++++++++++------------- 4 files changed, 396 insertions(+), 304 deletions(-) diff --git a/biophysics/CaConc.cpp b/biophysics/CaConc.cpp index 91dd8fce01..5e717184c7 100644 --- a/biophysics/CaConc.cpp +++ b/biophysics/CaConc.cpp @@ -14,43 +14,43 @@ const Cinfo* CaConc::initCinfo() { - static string doc[] = - { - "Name", "CaConc \n", - "Author", "Upinder S. Bhalla, 2014, NCBS \n", - "Description", "CaConc: Calcium concentration pool. Takes current from a \n" - "channel and keeps track of calcium buildup and depletion by a \n" - "single exponential process. \n", - }; - - static Dinfo< CaConc > dinfo; - - static Cinfo CaConcCinfo( - "CaConc", - CaConcBase::initCinfo(), - 0, - 0, - &dinfo, - doc, - sizeof(doc)/sizeof(string) - ); - - return &CaConcCinfo; + static string doc[] = + { + "Name", "CaConc \n", + "Author", "Upinder S. Bhalla, 2014, NCBS \n", + "Description", "CaConc: Calcium concentration pool. Takes current from a \n" + "channel and keeps track of calcium buildup and depletion by a \n" + "single exponential process. \n", + }; + + static Dinfo< CaConc > dinfo; + + static Cinfo CaConcCinfo( + "CaConc", + CaConcBase::initCinfo(), + 0, + 0, + &dinfo, + doc, + sizeof(doc)/sizeof(string) + ); + + return &CaConcCinfo; } /////////////////////////////////////////////////// static const Cinfo* caConcCinfo = CaConc::initCinfo(); CaConc::CaConc() - : CaConcBase(), - Ca_( 0.0 ), - CaBasal_( 0.0 ), - tau_( 1.0 ), - B_( 1.0 ), - c_( 0.0 ), - activation_( 0.0 ), - ceiling_( 1.0e9 ), - floor_( 0.0 ) + : CaConcBase(), + Ca_( 0.0 ), + CaBasal_( 0.0 ), + tau_( 1.0 ), + B_( 1.0 ), + c_( 0.0 ), + activation_( 0.0 ), + ceiling_( 1.0e9 ), + floor_( 0.0 ) {;} /////////////////////////////////////////////////// @@ -59,38 +59,38 @@ CaConc::CaConc() void CaConc::vSetCa( const Eref& e, double Ca ) { - Ca_ = Ca; + Ca_ = Ca; } double CaConc::vGetCa( const Eref& e ) const { - return Ca_; + return Ca_; } void CaConc::vSetCaBasal( const Eref& e, double CaBasal ) { - CaBasal_ = CaBasal; + CaBasal_ = CaBasal; } double CaConc::vGetCaBasal( const Eref& e ) const { - return CaBasal_; + return CaBasal_; } void CaConc::vSetTau( const Eref& e, double tau ) { - tau_ = tau; + tau_ = tau; } double CaConc::vGetTau( const Eref& e ) const { - return tau_; + return tau_; } void CaConc::vSetB( const Eref& e, double B ) { - B_ = B; + B_ = B; } double CaConc::vGetB( const Eref& e ) const { - return B_; + return B_; } void CaConc::vSetCeiling( const Eref& e, double ceiling ) { @@ -98,7 +98,7 @@ void CaConc::vSetCeiling( const Eref& e, double ceiling ) } double CaConc::vGetCeiling( const Eref& e ) const { - return ceiling_; + return ceiling_; } void CaConc::vSetFloor( const Eref& e, double floor ) @@ -107,7 +107,7 @@ void CaConc::vSetFloor( const Eref& e, double floor ) } double CaConc::vGetFloor( const Eref& e ) const { - return floor_; + return floor_; } /////////////////////////////////////////////////// @@ -116,45 +116,48 @@ double CaConc::vGetFloor( const Eref& e ) const void CaConc::vReinit( const Eref& e, ProcPtr p ) { - activation_ = 0.0; - c_ = 0.0; - Ca_ = CaBasal_; - concOut()->send( e, Ca_ ); + activation_ = 0.0; + c_ = 0.0; + Ca_ = CaBasal_; + concOut()->send( e, Ca_ ); } void CaConc::vProcess( const Eref& e, ProcPtr p ) { - double x = exp( -p->dt / tau_ ); - Ca_ = CaBasal_ + c_ * x + ( B_ * activation_ * tau_ ) * (1.0 - x); - if ( ceiling_ > 0.0 && Ca_ > ceiling_ ) { - Ca_ = ceiling_; - } else if ( Ca_ < floor_ ){ - Ca_ = floor_; - } - c_ = Ca_ - CaBasal_; - concOut()->send( e, Ca_ ); - activation_ = 0; + double x = exp( -p->dt / tau_ ); + Ca_ = CaBasal_ + c_ * x + ( B_ * activation_ * tau_ ) * (1.0 - x); + if ( ceiling_ > 0.0 && Ca_ > ceiling_ ) + { + Ca_ = ceiling_; + } + else if ( Ca_ < floor_ ) + { + Ca_ = floor_; + } + c_ = Ca_ - CaBasal_; + concOut()->send( e, Ca_ ); + activation_ = 0; } void CaConc::vCurrent( const Eref& e, double I ) { - activation_ += I; + activation_ += I; } void CaConc::vCurrentFraction( const Eref& e, double I, double fraction ) { - activation_ += I * fraction; + activation_ += I * fraction; } void CaConc::vIncrease( const Eref& e, double I ) { - activation_ += fabs( I ); + activation_ += fabs( I ); } void CaConc::vDecrease( const Eref& e, double I ) { - activation_ -= fabs( I ); + activation_ -= fabs( I ); } /////////////////////////////////////////////////// @@ -165,52 +168,52 @@ void CaConc::vDecrease( const Eref& e, double I ) void testCaConc() { /* - CaConc cc; - double tau = 0.10; - double basal = 0.0001; - - cc.setCa( basal ); - cc.setCaBasal( basal ); - cc.setTau( tau ); - // Here we use a volume of 1e-15 m^3, i.e., a 10 micron cube. - cc.setB( 5.2e-6 / 1e-15 ); - // Faraday constant = 96485.3415 s A / mol - // Use a 1 pA input current. This should give (0.5e-12/F) moles/sec - // influx, because Ca has valence of 2. - // So we get 5.2e-18 moles/sec coming in. - // Our volume is 1e-15 m^3 - // So our buildup should be at 5.2e-3 moles/m^3/sec = 5.2 uM/sec - double curr = 1e-12; - // This will settle when efflux = influx - // dC/dt = B*Ik - C/tau = 0. - // so Ca = CaBasal + tau * B * Ik = - // 0.0001 + 0.1 * 5.2e-6 * 1e3 = 0.000626 - - ProcInfo p; - p.dt = 0.001; - p.currTime = 0.0; + CaConc cc; + double tau = 0.10; + double basal = 0.0001; + + cc.setCa( basal ); + cc.setCaBasal( basal ); + cc.setTau( tau ); + // Here we use a volume of 1e-15 m^3, i.e., a 10 micron cube. + cc.setB( 5.2e-6 / 1e-15 ); + // Faraday constant = 96485.3415 s A / mol + // Use a 1 pA input current. This should give (0.5e-12/F) moles/sec + // influx, because Ca has valence of 2. + // So we get 5.2e-18 moles/sec coming in. + // Our volume is 1e-15 m^3 + // So our buildup should be at 5.2e-3 moles/m^3/sec = 5.2 uM/sec + double curr = 1e-12; + // This will settle when efflux = influx + // dC/dt = B*Ik - C/tau = 0. + // so Ca = CaBasal + tau * B * Ik = + // 0.0001 + 0.1 * 5.2e-6 * 1e3 = 0.000626 + + ProcInfo p; + p.dt = 0.001; + p.currTime = 0.0; Eref sheller(Id().eref()); Shell * shell = reinterpret_cast (sheller.data()); Id temp = shell->doCreate("CaConc", Id(), "caconc", 1); assert(temp.element()->getName() == "caconc"); - // Id tempId = Id::nextId(); - // Element temp( tempId, CaConc::initCinfo(), "temp", 0 ); - Eref er( &temp, 0 ); - cc.reinit( er, &p ); - - double y; - double conc; - double delta = 0.0; - for ( p.currTime = 0.0; p.currTime < 0.5; p.currTime += p.dt ) - { - cc.current( curr ); - cc.process( er, &p ); - y = basal + 526.0e-6 * ( 1.0 - exp( -p.currTime / tau ) ); - conc = cc.getCa(); - delta += ( y - conc ) * ( y - conc ); - } - assert( delta < 1e-6 ); - cout << "." << flush; + // Id tempId = Id::nextId(); + // Element temp( tempId, CaConc::initCinfo(), "temp", 0 ); + Eref er( &temp, 0 ); + cc.reinit( er, &p ); + + double y; + double conc; + double delta = 0.0; + for ( p.currTime = 0.0; p.currTime < 0.5; p.currTime += p.dt ) + { + cc.current( curr ); + cc.process( er, &p ); + y = basal + 526.0e-6 * ( 1.0 - exp( -p.currTime / tau ) ); + conc = cc.getCa(); + delta += ( y - conc ) * ( y - conc ); + } + assert( delta < 1e-6 ); + cout << "." << flush; */ } #endif diff --git a/biophysics/CaConc.h b/biophysics/CaConc.h index 5aadb1d62c..a7c9f41c98 100644 --- a/biophysics/CaConc.h +++ b/biophysics/CaConc.h @@ -35,44 +35,44 @@ class CaConc: public CaConcBase { - public: - CaConc(); - /////////////////////////////////////////////////////////////// - // Message handling functions - /////////////////////////////////////////////////////////////// - void vReinit( const Eref&, ProcPtr info ); - void vProcess( const Eref&, ProcPtr info ); +public: + CaConc(); + /////////////////////////////////////////////////////////////// + // Message handling functions + /////////////////////////////////////////////////////////////// + void vReinit( const Eref&, ProcPtr info ); + void vProcess( const Eref&, ProcPtr info ); - void vCurrent( const Eref& e, double I ); - void vCurrentFraction( const Eref& e, double I, double fraction ); - void vIncrease( const Eref& e, double I ); - void vDecrease( const Eref& e, double I ); - /////////////////////////////////////////////////////////////// - // Field handling functions - /////////////////////////////////////////////////////////////// - void vSetCa( const Eref& e, double val ); - double vGetCa( const Eref& e ) const; - void vSetCaBasal( const Eref& e, double val ); - double vGetCaBasal( const Eref& e ) const; - void vSetTau( const Eref& e, double val ); - double vGetTau( const Eref& e ) const; - void vSetB( const Eref& e, double val ); - double vGetB( const Eref& e ) const; - void vSetCeiling( const Eref& e, double val ); - double vGetCeiling( const Eref& e ) const; - void vSetFloor( const Eref& e, double val ); - double vGetFloor( const Eref& e ) const; + void vCurrent( const Eref& e, double I ); + void vCurrentFraction( const Eref& e, double I, double fraction ); + void vIncrease( const Eref& e, double I ); + void vDecrease( const Eref& e, double I ); + /////////////////////////////////////////////////////////////// + // Field handling functions + /////////////////////////////////////////////////////////////// + void vSetCa( const Eref& e, double val ); + double vGetCa( const Eref& e ) const; + void vSetCaBasal( const Eref& e, double val ); + double vGetCaBasal( const Eref& e ) const; + void vSetTau( const Eref& e, double val ); + double vGetTau( const Eref& e ) const; + void vSetB( const Eref& e, double val ); + double vGetB( const Eref& e ) const; + void vSetCeiling( const Eref& e, double val ); + double vGetCeiling( const Eref& e ) const; + void vSetFloor( const Eref& e, double val ); + double vGetFloor( const Eref& e ) const; - static const Cinfo* initCinfo(); - private: - double Ca_; - double CaBasal_; - double tau_; - double B_; - double c_; - double activation_; - double ceiling_; - double floor_; + static const Cinfo* initCinfo(); +private: + double Ca_; + double CaBasal_; + double tau_; + double B_; + double c_; + double activation_; + double ceiling_; + double floor_; }; diff --git a/biophysics/CaConcBase.cpp b/biophysics/CaConcBase.cpp index fb6d07ec9e..69d609df0f 100644 --- a/biophysics/CaConcBase.cpp +++ b/biophysics/CaConcBase.cpp @@ -58,23 +58,32 @@ const Cinfo* CaConcBase::initCinfo() static ElementValueFinfo< CaConcBase, double > Ca( "Ca", "Calcium concentration.", &CaConcBase::setCa, - &CaConcBase::getCa - ); + &CaConcBase::getCa); + + // NOTE: alias for Ca. NeuroML2 expect conc available on all + // ConncentrationModel. + // FIXME: If it is not needed after neuroml2 is fully supported, remove it + // else remove this fixme. + static ElementValueFinfo< CaConcBase, double > conc( "conc", + "Calcium concentration (alias for Ca)", + &CaConcBase::setCa, + &CaConcBase::getCa); + static ElementValueFinfo< CaConcBase, double > CaBasal( "CaBasal", "Basal Calcium concentration.", &CaConcBase::setCaBasal, - &CaConcBase::getCaBasal - ); + &CaConcBase::getCaBasal); + static ElementValueFinfo< CaConcBase, double > Ca_base( "Ca_base", "Basal Calcium concentration, synonym for CaBasal", &CaConcBase::setCaBasal, - &CaConcBase::getCaBasal - ); + &CaConcBase::getCaBasal); + static ElementValueFinfo< CaConcBase, double > tau( "tau", "Settling time for Ca concentration", &CaConcBase::setTau, - &CaConcBase::getTau - ); + &CaConcBase::getTau); + static ElementValueFinfo< CaConcBase, double > B( "B", "Volume scaling factor. " "Deprecated. This is a legacy field from GENESIS and exposes " @@ -82,36 +91,36 @@ const Cinfo* CaConcBase::initCinfo() "B = 1/(vol * F* VALENCE) so that it obeys:\n" "dC/dt = B*I_Ca - C/tau", &CaConcBase::setB, - &CaConcBase::getB - ); + &CaConcBase::getB); + static ElementValueFinfo< CaConcBase, double > thick( "thick", "Thickness of Ca shell, assumed cylindrical. Legal range is between 0 \n" "and the radius. If outside this range it is taken as the radius. \n" " Default zero, ie, the shell is the entire thickness of the cylinder \n", &CaConcBase::setThickness, - &CaConcBase::getThickness - ); + &CaConcBase::getThickness); + static ElementValueFinfo< CaConcBase, double > length( "length", "Length of Ca shell, assumed cylindrical", &CaConcBase::setLength, - &CaConcBase::getLength - ); + &CaConcBase::getLength); + static ElementValueFinfo< CaConcBase, double > diameter( "diameter", "Diameter of Ca shell, assumed cylindrical", &CaConcBase::setDiameter, - &CaConcBase::getDiameter - ); + &CaConcBase::getDiameter); + static ElementValueFinfo< CaConcBase, double > ceiling( "ceiling", "Ceiling value for Ca concentration. If Ca > ceiling, Ca = ceiling. \n" "If ceiling <= 0.0, there is no upper limit on Ca concentration value.", &CaConcBase::setCeiling, - &CaConcBase::getCeiling - ); + &CaConcBase::getCeiling); + static ElementValueFinfo< CaConcBase, double > floor( "floor", "Floor value for Ca concentration. If Ca < floor, Ca = floor", &CaConcBase::setFloor, - &CaConcBase::getFloor - ); + &CaConcBase::getFloor); + /////////////////////////////////////////////////////// // MsgDest definitions @@ -147,6 +156,7 @@ const Cinfo* CaConcBase::initCinfo() &proc, // Shared concOut(), // Src &Ca, // Value + &conc, // Value &CaBasal, // Value &Ca_base, // Value &tau, // Value diff --git a/python/moose/neuroml2/reader.py b/python/moose/neuroml2/reader.py index 9f4cf43fee..77d85e2634 100644 --- a/python/moose/neuroml2/reader.py +++ b/python/moose/neuroml2/reader.py @@ -8,8 +8,8 @@ # Maintainer: Dilawar Singh # Created: Wed Jul 24 15:55:54 2013 (+0530) # -# Notes: -# For update/log, please see git-blame documentation or browse the github +# Notes: +# For update/log, please see git-blame documentation or browse the github # repo https://github.com/BhallaLab/moose-core import os @@ -29,7 +29,8 @@ import neuroml.loaders as loaders from pyneuroml import pynml except: - print("********************************************************************") + print( + "********************************************************************") print("* ") print("* Please install libNeuroML & pyNeuroML: ") print("* pip install libneuroml") @@ -38,10 +39,12 @@ print("* Requirement for this is lxml: ") print("* apt-get install python-lxml") print("* ") - print("********************************************************************") + print( + "********************************************************************") # Utility functions + def sarea(comp): """ Return the surface area of compartment from length and @@ -62,12 +65,14 @@ def sarea(comp): else: return comp.diameter * comp.diameter * np.pi + def xarea(comp): """ Return the cross sectional area from diameter of the compartment. How to do it for spherical compartment?""" return comp.diameter * comp.diameter * np.pi / 4.0 + def setRa(comp, resistivity): """Calculate total raxial from specific value `resistivity`""" if comp.length > 0: @@ -75,9 +80,11 @@ def setRa(comp, resistivity): else: comp.Ra = resistivity * 8.0 / (comp.diameter * np.pi) + def setRm(comp, condDensity): """Set membrane resistance""" - comp.Rm = 1/(condDensity * sarea(comp)) + comp.Rm = 1 / (condDensity * sarea(comp)) + def setEk(comp, erev): """Set reversal potential""" @@ -89,12 +96,14 @@ def getSegments(nmlcell, component, sg_to_segments): sg = component.segment_groups #seg = component.segment if sg is None: - segments = nmlcell.morphology.segments - elif sg == 'all': # Special case - segments = [seg for seglist in sg_to_segments.values() for seg in seglist] + segments = nmlcell.morphology.segments + elif sg == 'all': # Special case + segments = [ + seg for seglist in sg_to_segments.values() for seg in seglist + ] else: segments = sg_to_segments[sg] - + unique_segs = [] unique_segs_ids = [] for s in segments: @@ -119,80 +128,96 @@ class NML2Reader(object): creates a passive neuronal morphology `/library/Purk2M9s`. """ def __init__(self, verbose=False): - self.lunit = 1e-6 # micron is the default length unit + # micron is the default length unit + self.lunit = 1e-6 + self.verbose = verbose self.doc = None - self.filename = None - self.nml_cells_to_moose = {} # NeuroML object to MOOSE object - self.nml_segs_to_moose = {} # NeuroML object to MOOSE object - self.nml_chans_to_moose = {} # NeuroML object to MOOSE object - self.nml_concs_to_moose = {} # NeuroML object to MOOSE object - self.moose_to_nml = {} # Moose object to NeuroML object - self.proto_cells = {} # map id to prototype cell in moose - self.proto_chans = {} # map id to prototype channels in moose - self.proto_pools = {} # map id to prototype pools (Ca2+, Mg2+) - self.includes = {} # Included files mapped to other readers - self.lib = moose.Neutral('/library') + self.filename = None + + self.nml_cells_to_moose = {} # NeuroML object to MOOSE object + self.nml_segs_to_moose = {} # NeuroML object to MOOSE object + self.nml_chans_to_moose = {} # NeuroML object to MOOSE object + self.nml_concs_to_moose = {} # NeuroML object to MOOSE object + self.moose_to_nml = {} # Moose object to NeuroML object + self.proto_cells = {} # map id to prototype cell in moose + self.proto_chans = {} # map id to prototype channels in moose + self.proto_pools = {} # map id to prototype pools (Ca2+, Mg2+) + self.includes = {} # Included files mapped to other readers + + # /library may have alreay been created. + if moose.exists('/library'): + self.lib = moose.element('/library') + else: + self.lib = moose.Neutral('/library') + self.id_to_ionChannel = {} - self._cell_to_sg = {} # nml cell to dict - the dict maps segment groups to segments - + + # nml cell to dict - the dict maps segment groups to segments + self._cell_to_sg = {} + self.cells_in_populations = {} self.pop_to_cell_type = {} self.seg_id_to_comp_name = {} self.paths_to_chan_elements = {} + def read(self, filename, symmetric=True): - self.doc = loaders.read_neuroml2_file(filename, include_includes=True, verbose=self.verbose) - + self.doc = loaders.read_neuroml2_file(filename, + include_includes=True, + verbose=self.verbose) + if self.verbose: - print('Parsed NeuroML2 file: %s'% filename) + print('Parsed NeuroML2 file: %s' % filename) self.filename = filename - - if len(self.doc.networks)>=1: + + if len(self.doc.networks) >= 1: self.network = self.doc.networks[0] - + moose.celsius = self._getTemperature() - + self.importConcentrationModels(self.doc) self.importIonChannels(self.doc) self.importInputs(self.doc) - - + for cell in self.doc.cells: self.createCellPrototype(cell, symmetric=symmetric) - - if len(self.doc.networks)>=1: + + if len(self.doc.networks) >= 1: self.createPopulations() self.createInputs() - print("Read all from %s"%filename) - + print("Read all from %s" % filename) + def _getTemperature(self): - if self.network.type=="networkWithTemperature": + if self.network.type == "networkWithTemperature": return SI(self.network.temperature) else: - return 0 # Why not, if there's no temp dependence in nml..? - + return 0 # Why not, if there's no temp dependence in nml..? + def getCellInPopulation(self, pop_id, index): return self.cells_in_populations[pop_id][index] - + def getComp(self, pop_id, cellIndex, segId): - return moose.element('%s/%s/%s/%s' % (self.lib.path, pop_id, cellIndex, self.seg_id_to_comp_name[self.pop_to_cell_type[pop_id]][segId])) - + return moose.element( + '%s/%s/%s/%s' % + (self.lib.path, pop_id, cellIndex, + self.seg_id_to_comp_name[self.pop_to_cell_type[pop_id]][segId])) + def createPopulations(self): for pop in self.network.populations: mpop = moose.Neutral('%s/%s' % (self.lib.path, pop.id)) - self.cells_in_populations[pop.id] ={} + self.cells_in_populations[pop.id] = {} for i in range(pop.size): - print("Creating %s/%s instances of %s under %s"%(i,pop.size,pop.component, mpop)) - self.pop_to_cell_type[pop.id]=pop.component - chid = moose.copy(self.proto_cells[pop.component], mpop, '%s'%(i)) - self.cells_in_populations[pop.id][i]=chid - - + print("Creating %s/%s instances of %s under %s" % + (i, pop.size, pop.component, mpop)) + self.pop_to_cell_type[pop.id] = pop.component + chid = moose.copy(self.proto_cells[pop.component], mpop, + '%s' % (i)) + self.cells_in_populations[pop.id][i] = chid + def getInput(self, input_id): - return moose.element('%s/inputs/%s'%(self.lib.path,input_id)) - - + return moose.element('%s/inputs/%s' % (self.lib.path, input_id)) + def createInputs(self): for el in self.network.explicit_inputs: pop_id = el.target.split('[')[0] @@ -201,13 +226,16 @@ def createInputs(self): if '/' in el.target: seg_id = el.target.split('/')[1] input = self.getInput(el.input) - moose.connect(input, 'output', self.getComp(pop_id,i,seg_id), 'injectMsg') - + moose.connect(input, 'output', self.getComp(pop_id, i, seg_id), + 'injectMsg') + for il in self.network.input_lists: for ii in il.input: input = self.getInput(il.component) - moose.connect(input, 'output', self.getComp(il.populations,ii.get_target_cell_id(),ii.get_segment_id()), 'injectMsg') - + moose.connect( + input, 'output', + self.getComp(il.populations, ii.get_target_cell_id(), + ii.get_segment_id()), 'injectMsg') def createCellPrototype(self, cell, symmetric=True): """To be completed - create the morphology, channels in prototype""" @@ -219,7 +247,6 @@ def createCellPrototype(self, cell, symmetric=True): self.importBiophysics(cell, nrn) return cell, nrn - def createMorphology(self, nmlcell, moosecell, symmetric=True): """Create the MOOSE compartmental morphology in `moosecell` using the segments in NeuroML2 cell `nmlcell`. Create symmetric @@ -228,7 +255,7 @@ def createMorphology(self, nmlcell, moosecell, symmetric=True): """ morphology = nmlcell.morphology segments = morphology.segments - id_to_segment = dict([(seg.id, seg) for seg in segments]) + id_to_segment = dict([(seg.id, seg) for seg in segments]) if symmetric: compclass = moose.SymCompartment else: @@ -237,13 +264,13 @@ def createMorphology(self, nmlcell, moosecell, symmetric=True): # naming convention does not clash with that in MOOSE cellpath = moosecell.path id_to_comp = {} - self.seg_id_to_comp_name[nmlcell.id]={} + self.seg_id_to_comp_name[nmlcell.id] = {} for seg in segments: if seg.name is not None: id_to_comp[seg.id] = compclass('%s/%s' % (cellpath, seg.name)) self.seg_id_to_comp_name[nmlcell.id][seg.id] = seg.name else: - name = 'comp_%s'%seg.id + name = 'comp_%s' % seg.id id_to_comp[seg.id] = compclass('%s/%s' % (cellpath, name)) self.seg_id_to_comp_name[nmlcell.id][seg.id] = name # Now assign the positions and connect up axial resistance @@ -258,39 +285,47 @@ def createMorphology(self, nmlcell, moosecell, symmetric=True): except AttributeError: parent = None self.moose_to_nml[comp] = segment - self.nml_segs_to_moose[segment.id] = comp - p0 = segment.proximal + self.nml_segs_to_moose[segment.id] = comp + p0 = segment.proximal if p0 is None: if parent: p0 = parent.distal else: - raise Exception('No proximal point and no parent segment for segment: name=%s, id=%s' % (segment.name, segment.id)) - comp.x0, comp.y0, comp.z0 = (x * self.lunit for x in map(float, (p0.x, p0.y, p0.z))) + raise Exception( + 'No proximal point and no parent segment for segment: name=%s, id=%s' + % (segment.name, segment.id)) + comp.x0, comp.y0, comp.z0 = (x * self.lunit + for x in map(float, (p0.x, p0.y, + p0.z))) p1 = segment.distal - comp.x, comp.y, comp.z = (x * self.lunit for x in map(float, (p1.x, p1.y, p1.z))) - comp.length = np.sqrt((comp.x - comp.x0)**2 - + (comp.y - comp.y0)**2 - + (comp.z - comp.z0)**2) + comp.x, comp.y, comp.z = (x * self.lunit + for x in map(float, (p1.x, p1.y, p1.z))) + comp.length = np.sqrt((comp.x - comp.x0)**2 + + (comp.y - comp.y0)**2 + + (comp.z - comp.z0)**2) # This can pose problem with moose where both ends of # compartment have same diameter. We are averaging the two # - may be splitting the compartment into two is better? - comp.diameter = (float(p0.diameter)+float(p1.diameter)) * self.lunit / 2 + comp.diameter = (float(p0.diameter) + + float(p1.diameter)) * self.lunit / 2 if parent: pcomp = id_to_comp[parent.id] moose.connect(comp, src, pcomp, dst) - sg_to_segments = {} + sg_to_segments = {} for sg in morphology.segment_groups: - sg_to_segments[sg.id] = [id_to_segment[m.segments] for m in sg.members] + sg_to_segments[sg.id] = [ + id_to_segment[m.segments] for m in sg.members + ] for sg in morphology.segment_groups: if not sg.id in sg_to_segments: sg_to_segments[sg.id] = [] for inc in sg.includes: for cseg in sg_to_segments[inc.segment_groups]: sg_to_segments[sg.id].append(cseg) - + if not 'all' in sg_to_segments: - sg_to_segments['all'] = [ s for s in segments ] - + sg_to_segments['all'] = [s for s in segments] + self._cell_to_sg[nmlcell.id] = sg_to_segments return id_to_comp, id_to_segment, sg_to_segments @@ -299,10 +334,13 @@ def importBiophysics(self, nmlcell, moosecell): according to NeuroML2 cell `nmlcell`.""" bp = nmlcell.biophysical_properties if bp is None: - print('Warning: %s in %s has no biophysical properties' % (nmlcell.id, self.filename)) + print('Warning: %s in %s has no biophysical properties' % + (nmlcell.id, self.filename)) return - self.importMembraneProperties(nmlcell, moosecell, bp.membrane_properties) - self.importIntracellularProperties(nmlcell, moosecell, bp.intracellular_properties) + self.importMembraneProperties(nmlcell, moosecell, + bp.membrane_properties) + self.importIntracellularProperties(nmlcell, moosecell, + bp.intracellular_properties) def importMembraneProperties(self, nmlcell, moosecell, mp): """Create the membrane properties from nmlcell in moosecell""" @@ -319,14 +357,14 @@ def importCapacitances(self, nmlcell, moosecell, specificCapacitances): for seg in sg_to_segments[specific_cm.segment_groups]: comp = self.nml_segs_to_moose[seg.id] comp.Cm = sarea(comp) * cm - + def importInitMembPotential(self, nmlcell, moosecell, membraneProperties): sg_to_segments = self._cell_to_sg[nmlcell.id] for imp in membraneProperties.init_memb_potentials: initv = SI(imp.value) for seg in sg_to_segments[imp.segment_groups]: comp = self.nml_segs_to_moose[seg.id] - comp.initVm = initv + comp.initVm = initv def importIntracellularProperties(self, nmlcell, moosecell, properties): self.importAxialResistance(nmlcell, properties) @@ -336,29 +374,33 @@ def importSpecies(self, nmlcell, properties): sg_to_segments = self._cell_to_sg[nmlcell.id] for species in properties.species: if (species.concentration_model is not None) and \ - (species.concentration_model.id not in self.proto_pools): + (species.concentration_model not in self.proto_pools): continue segments = getSegments(nmlcell, species, sg_to_segments) for seg in segments: - comp = self.nml_segs_to_moose[seg.id] + comp = self.nml_segs_to_moose[seg.id] self.copySpecies(species, comp) def copySpecies(self, species, compartment): """Copy the prototype pool `species` to compartment. Currently only decaying pool of Ca2+ supported""" proto_pool = None - if species.concentrationModel in self.proto_pools: + if species.concentration_model in self.proto_pools: proto_pool = self.proto_pools[species.concentration_model] else: for innerReader in self.includes.values(): - if species.concentrationModel in innerReader.proto_pools: - proto_pool = innerReader.proto_pools[species.concentrationModel] + if species.concentration_model in innerReader.proto_pools: + proto_pool = innerReader.proto_pools[ + species.concentration_model] break if not proto_pool: - raise Exception('No prototype pool for %s referred to by %s' % (species.concentration_model, species.id)) - pool_id = moose.copy(proto_pool, comp, species.id) + raise Exception('No prototype pool for %s referred to by %s' % + (species.concentration_model, species.id)) + pool_id = moose.copy(proto_pool, compartment, species.id) pool = moose.element(pool_id) - pool.B = pool.B / (np.pi * compartment.length * (0.5 * compartment.diameter + pool.thickness) * (0.5 * compartment.diameter - pool.thickness)) + pool.B = pool.B / (np.pi * compartment.length * + (0.5 * compartment.diameter + pool.thick) * + (0.5 * compartment.diameter - pool.thick)) return pool def importAxialResistance(self, nmlcell, intracellularProperties): @@ -367,43 +409,52 @@ def importAxialResistance(self, nmlcell, intracellularProperties): segments = getSegments(nmlcell, r, sg_to_segments) for seg in segments: comp = self.nml_segs_to_moose[seg.id] - setRa(comp, SI(r.value)) - - def isPassiveChan(self,chan): + setRa(comp, SI(r.value)) + + def isPassiveChan(self, chan): if chan.type == 'ionChannelPassive': return True - if hasattr(chan,'gates'): - return len(chan.gate_hh_rates)+len(chan.gates)==0 + if hasattr(chan, 'gates'): + return len(chan.gate_hh_rates) + len(chan.gates) == 0 return False - rate_fn_map = { 'HHExpRate': exponential2, 'HHSigmoidRate': sigmoid2, 'HHSigmoidVariable': sigmoid2, - 'HHExpLinearRate': linoid2 } + 'HHExpLinearRate': linoid2 + } def calculateRateFn(self, ratefn, vmin, vmax, tablen=3000, vShift='0mV'): """Returns A / B table from ngate.""" tab = np.linspace(vmin, vmax, tablen) if self._is_standard_nml_rate(ratefn): - midpoint, rate, scale = map(SI, (ratefn.midpoint, ratefn.rate, ratefn.scale)) + midpoint, rate, scale = map( + SI, (ratefn.midpoint, ratefn.rate, ratefn.scale)) return self.rate_fn_map[ratefn.type](tab, rate, scale, midpoint) - else: - for ct in self.doc.ComponentType: - if ratefn.type == ct.name: - print("Using %s to evaluate rate"%ct.name) - rate = [] - for v in tab: - vals = pynml.evaluate_component(ct,req_variables={'v':'%sV'%v,'vShift':vShift,'temperature':self._getTemperature()}) - '''print vals''' - if 'x' in vals: - rate.append(vals['x']) - if 't' in vals: - rate.append(vals['t']) - if 'r' in vals: - rate.append(vals['r']) - return np.array(rate) + + for ct in self.doc.ComponentType: + if ratefn.type != ct.name: + continue + logger_.info("Using %s to evaluate rate" % ct.name) + rate = [] + for v in tab: + vars = { + 'v': '%sV' % v, + 'vShift': vShift, + 'temperature': self._getTemperature() + } + for k in self.nml_concs_to_moose: + vars.update({k: self.nml_concs_to_moose[k].conc}) + vals = pynml.evaluate_component(ct, req_variables=vars) + '''print(vals)''' + if 'x' in vals: + rate.append(vals['x']) + if 't' in vals: + rate.append(vals['t']) + if 'r' in vals: + rate.append(vals['r']) + return np.array(rate) def importChannelsToCell(self, nmlcell, moosecell, membrane_properties): sg_to_segments = self._cell_to_sg[nmlcell.id] @@ -414,12 +465,15 @@ def importChannelsToCell(self, nmlcell, moosecell, membrane_properties): try: ionChannel = self.id_to_ionChannel[chdens.ion_channel] except KeyError: - print('No channel with id', chdens.ion_channel) + print('No channel with id', chdens.ion_channel) continue - + if self.verbose: - print('Setting density of channel %s in %s to %s; erev=%s (passive: %s)'%(chdens.id, segments, condDensity,erev,self.isPassiveChan(ionChannel))) - + print( + 'Setting density of channel %s in %s to %s; erev=%s (passive: %s)' + % (chdens.id, segments, condDensity, erev, + self.isPassiveChan(ionChannel))) + if self.isPassiveChan(ionChannel): for seg in segments: comp = self.nml_segs_to_moose[seg.id] @@ -427,7 +481,8 @@ def importChannelsToCell(self, nmlcell, moosecell, membrane_properties): setEk(comp, erev) else: for seg in segments: - self.copyChannel(chdens, self.nml_segs_to_moose[seg.id], condDensity, erev) + self.copyChannel(chdens, self.nml_segs_to_moose[seg.id], + condDensity, erev) '''moose.le(self.nml_segs_to_moose[seg.id]) moose.showfield(self.nml_segs_to_moose[seg.id], field="*", showtype=True)''' @@ -445,22 +500,25 @@ def copyChannel(self, chdens, comp, condDensity, erev): proto_chan = innerReader.proto_chans[chdens.ion_channel] break if not proto_chan: - raise Exception('No prototype channel for %s referred to by %s' % (chdens.ion_channel, chdens.id)) + raise Exception('No prototype channel for %s referred to by %s' % + (chdens.ion_channel, chdens.id)) if self.verbose: - print('Copying %s to %s, %s; erev=%s'%(chdens.id, comp, condDensity, erev)) + print('Copying %s to %s, %s; erev=%s' % + (chdens.id, comp, condDensity, erev)) orig = chdens.id chid = moose.copy(proto_chan, comp, chdens.id) chan = moose.element(chid) els = list(self.paths_to_chan_elements.keys()) for p in els: - pp = p.replace('%s/'%chdens.ion_channel,'%s/'%orig) - self.paths_to_chan_elements[pp] = self.paths_to_chan_elements[p].replace('%s/'%chdens.ion_channel,'%s/'%orig) + pp = p.replace('%s/' % chdens.ion_channel, '%s/' % orig) + self.paths_to_chan_elements[pp] = self.paths_to_chan_elements[ + p].replace('%s/' % chdens.ion_channel, '%s/' % orig) #print(self.paths_to_chan_elements) chan.Gbar = sarea(comp) * condDensity chan.Ek = erev moose.connect(chan, 'channel', comp, 'channel') - return chan + return chan ''' def importIncludes(self, doc): @@ -487,7 +545,7 @@ def importIncludes(self, doc): if error: print(self.filename, 'Last exception:', error) raise IOError('Could not read any of the locations: %s' % (paths))''' - + def _is_standard_nml_rate(self, rate): return rate.type=='HHExpLinearRate' \ or rate.type=='HHExpRate' or \ @@ -497,12 +555,14 @@ def _is_standard_nml_rate(self, rate): def createHHChannel(self, chan, vmin=-150e-3, vmax=100e-3, vdivs=5000): mchan = moose.HHChannel('%s/%s' % (self.lib.path, chan.id)) mgates = map(moose.element, (mchan.gateX, mchan.gateY, mchan.gateZ)) - assert(len(chan.gate_hh_rates) <= 3) # We handle only up to 3 gates in HHCHannel - + assert (len(chan.gate_hh_rates) <= 3 + ) # We handle only up to 3 gates in HHCHannel + if self.verbose: - print('== Creating channel: %s (%s) -> %s (%s)'%(chan.id, chan.gate_hh_rates, mchan, mgates)) + print('== Creating channel: %s (%s) -> %s (%s)' % + (chan.id, chan.gate_hh_rates, mchan, mgates)) all_gates = chan.gates + chan.gate_hh_rates - for ngate, mgate in zip(all_gates,mgates): + for ngate, mgate in zip(all_gates, mgates): if mgate.name.endswith('X'): mchan.Xpower = ngate.instances elif mgate.name.endswith('Y'): @@ -522,27 +582,37 @@ def createHHChannel(self, chan, vmin=-150e-3, vmax=100e-3, vdivs=5000): # refering to tau_inf and m_inf?? fwd = ngate.forward_rate rev = ngate.reverse_rate - - self.paths_to_chan_elements['%s/%s'%(chan.id,ngate.id)] = '%s/%s'%(chan.id,mgate.name) - + + self.paths_to_chan_elements[ + '%s/%s' % + (chan.id, ngate.id)] = '%s/%s' % (chan.id, mgate.name) + q10_scale = 1 if ngate.q10_settings: if ngate.q10_settings.type == 'q10Fixed': - q10_scale= float(ngate.q10_settings.fixed_q10) + q10_scale = float(ngate.q10_settings.fixed_q10) elif ngate.q10_settings.type == 'q10ExpTemp': - q10_scale = math.pow(float(ngate.q10_settings.q10_factor),(self._getTemperature()- SI(ngate.q10_settings.experimental_temp))/10) + q10_scale = math.pow( + float(ngate.q10_settings.q10_factor), + (self._getTemperature() - + SI(ngate.q10_settings.experimental_temp)) / 10) #print('Q10: %s; %s; %s; %s'%(ngate.q10_settings.q10_factor, self._getTemperature(),SI(ngate.q10_settings.experimental_temp),q10_scale)) else: - raise Exception('Unknown Q10 scaling type %s: %s'%(ngate.q10_settings.type,ngate.q10_settings)) - + raise Exception( + 'Unknown Q10 scaling type %s: %s' % + (ngate.q10_settings.type, ngate.q10_settings)) + if self.verbose: - print(' === Gate: %s; %s; %s; %s; %s; scale=%s'%(ngate.id, mgate.path, mchan.Xpower, fwd, rev, q10_scale)) - + print( + ' === Gate: %s; %s; %s; %s; %s; scale=%s' % + (ngate.id, mgate.path, mchan.Xpower, fwd, rev, q10_scale)) + if (fwd is not None) and (rev is not None): alpha = self.calculateRateFn(fwd, vmin, vmax, vdivs) beta = self.calculateRateFn(rev, vmin, vmax, vdivs) mgate.tableA = q10_scale * (alpha) mgate.tableB = q10_scale * (alpha + beta) + # Assuming the meaning of the elements in GateHHTauInf ... if hasattr(ngate,'time_course') and hasattr(ngate,'steady_state') \ and (ngate.time_course is not None) and (ngate.steady_state is not None): @@ -552,15 +622,17 @@ def createHHChannel(self, chan, vmin=-150e-3, vmax=100e-3, vdivs=5000): inf = self.calculateRateFn(inf, vmin, vmax, vdivs) mgate.tableA = q10_scale * (inf / tau) mgate.tableB = q10_scale * (1 / tau) - - if hasattr(ngate,'steady_state') and (ngate.time_course is None) and (ngate.steady_state is not None): + + if hasattr(ngate, + 'steady_state') and (ngate.time_course is None) and ( + ngate.steady_state is not None): inf = ngate.steady_state tau = 1 / (alpha + beta) if (inf is not None): inf = self.calculateRateFn(inf, vmin, vmax, vdivs) mgate.tableA = q10_scale * (inf / tau) mgate.tableB = q10_scale * (1 / tau) - + if self.verbose: print(self.filename, '== Created', mchan.path, 'for', chan.id) return mchan @@ -573,39 +645,43 @@ def createPassiveChannel(self, chan): def importInputs(self, doc): minputs = moose.Neutral('%s/inputs' % (self.lib.path)) - for pg_nml in doc.pulse_generators: + for pg_nml in doc.pulse_generators: + assert pg_nml.id pg = moose.PulseGen('%s/%s' % (minputs.path, pg_nml.id)) pg.firstDelay = SI(pg_nml.delay) pg.firstWidth = SI(pg_nml.duration) pg.firstLevel = SI(pg_nml.amplitude) pg.secondDelay = 1e9 - def importIonChannels(self, doc, vmin=-150e-3, vmax=100e-3, vdivs=5000): if self.verbose: print(self.filename, 'Importing the ion channels') - - for chan in doc.ion_channel+doc.ion_channel_hhs: + + for chan in doc.ion_channel + doc.ion_channel_hhs: if chan.type == 'ionChannelHH': mchan = self.createHHChannel(chan) elif self.isPassiveChan(chan): mchan = self.createPassiveChannel(chan) else: mchan = self.createHHChannel(chan) - + + assert chan.id self.id_to_ionChannel[chan.id] = chan self.nml_chans_to_moose[chan.id] = mchan self.proto_chans[chan.id] = mchan if self.verbose: - print(self.filename, 'Created ion channel', mchan.path, 'for', chan.type, chan.id) + print(self.filename, 'Created ion channel', mchan.path, 'for', + chan.type, chan.id) def importConcentrationModels(self, doc): for concModel in doc.decaying_pool_concentration_models: - proto = self.createDecayingPoolConcentrationModel(concModel) + # proto = self.createDecayingPoolConcentrationModel(concModel) + self.createDecayingPoolConcentrationModel(concModel) def createDecayingPoolConcentrationModel(self, concModel): - """Create prototype for concentration model""" + """Create prototype for concentration model""" + assert concModel.id name = concModel.id if hasattr(concModel, 'name') and concModel.name is not None: name = concModel.name @@ -614,8 +690,11 @@ def createDecayingPoolConcentrationModel(self, concModel): ca.CaBasal = SI(concModel.resting_conc) ca.tau = SI(concModel.decay_constant) ca.thick = SI(concModel.shell_thickness) - ca.B = 5.2e-6 # B = 5.2e-6/(Ad) where A is the area of the shell and d is thickness - must divide by shell volume when copying + + # B = 5.2e-6/(Ad) where A is the area of the shell and d is thickness - must divide by shell volume when copying + ca.B = 5.2e-6 self.proto_pools[concModel.id] = ca self.nml_concs_to_moose[concModel.id] = ca self.moose_to_nml[ca] = concModel - logger_.debug('Created moose element: %s for nml conc %s' % (ca.path, concModel.id)) + logger_.debug('Created moose element: %s for nml conc %s' % + (ca.path, concModel.id)) From 8a0bd8e32e0ba9cd2dc3d260439de1d18a6217e5 Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Thu, 12 Mar 2020 10:19:08 +0530 Subject: [PATCH 11/16] - If nml user has set id of the gate to be 'x', 'y' or 'z' then load the dynamics into HHChannels's gateX, gateY, or gateZ dyamics. - removed temp changes made to CaConcBase. It is not needed. --- biophysics/CaConc.h | 4 +- biophysics/CaConcBase.cpp | 74 +++-- biophysics/CaConcBase.h | 156 +++++------ python/moose/neuroml2/reader.py | 331 +++++++++++++++++------ tests/support/CaPool.nml | 10 + tests/support/MScellupdated_primDend.nml | 113 ++++++++ tests/support/SKchannel2.nml | 107 ++++++++ tests/support/pas.channel.nml | 12 + tests/support/test_nml2.py | 160 +++++++++++ 9 files changed, 758 insertions(+), 209 deletions(-) create mode 100644 tests/support/CaPool.nml create mode 100644 tests/support/MScellupdated_primDend.nml create mode 100644 tests/support/SKchannel2.nml create mode 100644 tests/support/pas.channel.nml create mode 100644 tests/support/test_nml2.py diff --git a/biophysics/CaConc.h b/biophysics/CaConc.h index a7c9f41c98..66318d64ee 100644 --- a/biophysics/CaConc.h +++ b/biophysics/CaConc.h @@ -16,8 +16,8 @@ * without diffusion. It uses a simple exponential return of Ca * to baseline, with influxes from ion channels. It solves the * equation: - * dC/dt = B*Ik - C/tau - * where Ca = Ca_base + C. + * + * dC/dt = B*Ik - C/tau, where Ca = Ca_base + C. * * From the GENESIS notes: * In SI units, where concentration is moles/m^3 diff --git a/biophysics/CaConcBase.cpp b/biophysics/CaConcBase.cpp index 69d609df0f..e1b650c499 100644 --- a/biophysics/CaConcBase.cpp +++ b/biophysics/CaConcBase.cpp @@ -33,58 +33,49 @@ SrcFinfo1< double >* CaConcBase::concOut() const Cinfo* CaConcBase::initCinfo() { - /////////////////////////////////////////////////////// // Shared message definitions - /////////////////////////////////////////////////////// - static DestFinfo process( "process", - "Handles process call", - new ProcOpFunc< CaConcBase >( &CaConcBase::process ) ); - static DestFinfo reinit( "reinit", - "Handles reinit call", - new ProcOpFunc< CaConcBase >( &CaConcBase::reinit ) ); + static DestFinfo process("process", + "Handles process call", + new ProcOpFunc< CaConcBase >( &CaConcBase::process ) + ); + + static DestFinfo reinit("reinit", + "Handles reinit call", + new ProcOpFunc< CaConcBase >( &CaConcBase::reinit ) + ); static Finfo* processShared[] = { &process, &reinit }; - static SharedFinfo proc( "proc", - "Shared message to receive Process message from scheduler", - processShared, sizeof( processShared ) / sizeof( Finfo* ) ); + static SharedFinfo proc("proc", + "Shared message to receive Process message from scheduler", + processShared, sizeof( processShared ) / sizeof( Finfo* ) + ); - /////////////////////////////////////////////////////// // Field definitions - /////////////////////////////////////////////////////// static ElementValueFinfo< CaConcBase, double > Ca( "Ca", "Calcium concentration.", &CaConcBase::setCa, &CaConcBase::getCa); - // NOTE: alias for Ca. NeuroML2 expect conc available on all - // ConncentrationModel. - // FIXME: If it is not needed after neuroml2 is fully supported, remove it - // else remove this fixme. - static ElementValueFinfo< CaConcBase, double > conc( "conc", - "Calcium concentration (alias for Ca)", - &CaConcBase::setCa, - &CaConcBase::getCa); - - static ElementValueFinfo< CaConcBase, double > CaBasal( "CaBasal", + static ElementValueFinfo< CaConcBase, double > CaBasal("CaBasal", "Basal Calcium concentration.", &CaConcBase::setCaBasal, &CaConcBase::getCaBasal); - static ElementValueFinfo< CaConcBase, double > Ca_base( "Ca_base", + static ElementValueFinfo< CaConcBase, double > Ca_base("Ca_base", "Basal Calcium concentration, synonym for CaBasal", &CaConcBase::setCaBasal, &CaConcBase::getCaBasal); - static ElementValueFinfo< CaConcBase, double > tau( "tau", + static ElementValueFinfo< CaConcBase, double > tau("tau", "Settling time for Ca concentration", &CaConcBase::setTau, &CaConcBase::getTau); - static ElementValueFinfo< CaConcBase, double > B( "B", + static ElementValueFinfo< CaConcBase, double > B("B", "Volume scaling factor. " "Deprecated. This is a legacy field from GENESIS and exposes " "internal calculations. Please do not use. \n" @@ -127,36 +118,35 @@ const Cinfo* CaConcBase::initCinfo() /////////////////////////////////////////////////////// static DestFinfo current( "current", - "Calcium Ion current, due to be converted to conc.", - new EpFunc1< CaConcBase, double >( &CaConcBase::current ) - ); + "Calcium Ion current, due to be converted to conc.", + new EpFunc1< CaConcBase, double >( &CaConcBase::current ) + ); static DestFinfo currentFraction( "currentFraction", - "Fraction of total Ion current, that is carried by Ca2+.", - new EpFunc2< CaConcBase, double, double >( &CaConcBase::currentFraction ) - ); + "Fraction of total Ion current, that is carried by Ca2+.", + new EpFunc2< CaConcBase, double, double >( &CaConcBase::currentFraction ) + ); static DestFinfo increase( "increase", - "Any input current that increases the concentration.", - new EpFunc1< CaConcBase, double >( &CaConcBase::increase ) - ); + "Any input current that increases the concentration.", + new EpFunc1< CaConcBase, double >( &CaConcBase::increase ) + ); static DestFinfo decrease( "decrease", - "Any input current that decreases the concentration.", - new EpFunc1< CaConcBase, double >( &CaConcBase::decrease ) - ); + "Any input current that decreases the concentration.", + new EpFunc1< CaConcBase, double >( &CaConcBase::decrease ) + ); static DestFinfo basal( "basal", - "Synonym for assignment of basal conc.", - new EpFunc1< CaConcBase, double >( &CaConcBase::setCaBasal ) - ); + "Synonym for assignment of basal conc.", + new EpFunc1< CaConcBase, double >( &CaConcBase::setCaBasal ) + ); static Finfo* CaConcBaseFinfos[] = { &proc, // Shared concOut(), // Src &Ca, // Value - &conc, // Value &CaBasal, // Value &Ca_base, // Value &tau, // Value diff --git a/biophysics/CaConcBase.h b/biophysics/CaConcBase.h index c215e84e52..b7f017d6a3 100644 --- a/biophysics/CaConcBase.h +++ b/biophysics/CaConcBase.h @@ -37,91 +37,91 @@ class CaConcBase { - public: - CaConcBase(); - /////////////////////////////////////////////////////////////// - // Message handling functions - /////////////////////////////////////////////////////////////// - void reinit( const Eref&, ProcPtr info ); - void process( const Eref&, ProcPtr info ); +public: + CaConcBase(); + /////////////////////////////////////////////////////////////// + // Message handling functions + /////////////////////////////////////////////////////////////// + void reinit( const Eref&, ProcPtr info ); + void process( const Eref&, ProcPtr info ); - void current( const Eref& e, double I ); - void currentFraction( const Eref& e, double I, double fraction ); - void increase( const Eref& e, double I ); - void decrease( const Eref& e, double I ); - /////////////////////////////////////////////////////////////// - // Virtual Message handling functions - /////////////////////////////////////////////////////////////// - virtual void vReinit( const Eref&, ProcPtr info ) = 0; - virtual void vProcess( const Eref&, ProcPtr info ) = 0; + void current( const Eref& e, double I ); + void currentFraction( const Eref& e, double I, double fraction ); + void increase( const Eref& e, double I ); + void decrease( const Eref& e, double I ); + /////////////////////////////////////////////////////////////// + // Virtual Message handling functions + /////////////////////////////////////////////////////////////// + virtual void vReinit( const Eref&, ProcPtr info ) = 0; + virtual void vProcess( const Eref&, ProcPtr info ) = 0; - virtual void vCurrent( const Eref& e, double I ) = 0; - virtual void vCurrentFraction( const Eref& e, double I, double fraction ) = 0; - virtual void vIncrease( const Eref& e, double I ) = 0; - virtual void vDecrease( const Eref& e, double I ) = 0; - /////////////////////////////////////////////////////////////// - // Field handling functions - /////////////////////////////////////////////////////////////// - void setCa( const Eref& e, double val ); - double getCa( const Eref& e ) const; - void setCaBasal( const Eref& e, double val ); - double getCaBasal( const Eref& e ) const; - void setTau( const Eref& e, double val ); - double getTau( const Eref& e ) const; - void setB( const Eref& e, double val ); - double getB( const Eref& e ) const; - void setCeiling( const Eref& e, double val ); - double getCeiling( const Eref& e ) const; - void setFloor( const Eref& e, double val ); - double getFloor( const Eref& e ) const; - void setThickness( const Eref& e, double val ); - double getThickness( const Eref& e ) const; - void setLength( const Eref& e, double val ); - double getLength( const Eref& e ) const; - void setDiameter( const Eref& e, double val ); - double getDiameter( const Eref& e ) const; - /////////////////////////////////////////////////////////////// - // Virtual Field handling functions - /////////////////////////////////////////////////////////////// - virtual void vSetCa( const Eref& e, double val ) = 0; - virtual double vGetCa( const Eref& e ) const = 0; - virtual void vSetCaBasal( const Eref& e, double val ) = 0; - virtual double vGetCaBasal( const Eref& e ) const = 0; - virtual void vSetTau( const Eref& e, double val ) = 0; - virtual double vGetTau( const Eref& e ) const = 0; - virtual void vSetB( const Eref& e, double val ) = 0; - virtual double vGetB( const Eref& e ) const = 0; - virtual void vSetCeiling( const Eref& e, double val ) = 0; - virtual double vGetCeiling( const Eref& e ) const = 0; - virtual void vSetFloor( const Eref& e, double val ) = 0; - virtual double vGetFloor( const Eref& e ) const = 0; + virtual void vCurrent( const Eref& e, double I ) = 0; + virtual void vCurrentFraction( const Eref& e, double I, double fraction ) = 0; + virtual void vIncrease( const Eref& e, double I ) = 0; + virtual void vDecrease( const Eref& e, double I ) = 0; + /////////////////////////////////////////////////////////////// + // Field handling functions + /////////////////////////////////////////////////////////////// + void setCa( const Eref& e, double val ); + double getCa( const Eref& e ) const; + void setCaBasal( const Eref& e, double val ); + double getCaBasal( const Eref& e ) const; + void setTau( const Eref& e, double val ); + double getTau( const Eref& e ) const; + void setB( const Eref& e, double val ); + double getB( const Eref& e ) const; + void setCeiling( const Eref& e, double val ); + double getCeiling( const Eref& e ) const; + void setFloor( const Eref& e, double val ); + double getFloor( const Eref& e ) const; + void setThickness( const Eref& e, double val ); + double getThickness( const Eref& e ) const; + void setLength( const Eref& e, double val ); + double getLength( const Eref& e ) const; + void setDiameter( const Eref& e, double val ); + double getDiameter( const Eref& e ) const; + /////////////////////////////////////////////////////////////// + // Virtual Field handling functions + /////////////////////////////////////////////////////////////// + virtual void vSetCa( const Eref& e, double val ) = 0; + virtual double vGetCa( const Eref& e ) const = 0; + virtual void vSetCaBasal( const Eref& e, double val ) = 0; + virtual double vGetCaBasal( const Eref& e ) const = 0; + virtual void vSetTau( const Eref& e, double val ) = 0; + virtual double vGetTau( const Eref& e ) const = 0; + virtual void vSetB( const Eref& e, double val ) = 0; + virtual double vGetB( const Eref& e ) const = 0; + virtual void vSetCeiling( const Eref& e, double val ) = 0; + virtual double vGetCeiling( const Eref& e ) const = 0; + virtual void vSetFloor( const Eref& e, double val ) = 0; + virtual double vGetFloor( const Eref& e ) const = 0; - /////////////////////////////////////////////////////////////// - // Utility function in case length, dia or thickness is updated - void updateDimensions( const Eref& e ); + /////////////////////////////////////////////////////////////// + // Utility function in case length, dia or thickness is updated + void updateDimensions( const Eref& e ); - /// Used to set up the solver. Dummy for regular classes. - virtual void vSetSolver( const Eref& e, Id hsolve ); + /// Used to set up the solver. Dummy for regular classes. + virtual void vSetSolver( const Eref& e, Id hsolve ); - /** - * Swaps Cinfos in order to make Zombies. - */ - static void zombify( Element* orig, const Cinfo* zClass, - Id hsolve ); + /** + * Swaps Cinfos in order to make Zombies. + */ + static void zombify( Element* orig, const Cinfo* zClass, + Id hsolve ); - /* - * This Finfo is used to send out Ca concentration to channels. - * - * It is exposed here so that HSolve can also use it to send out - * Ca concentration to the recipients. - */ - static SrcFinfo1< double >* concOut(); + /* + * This Finfo is used to send out Ca concentration to channels. + * + * It is exposed here so that HSolve can also use it to send out + * Ca concentration to the recipients. + */ + static SrcFinfo1< double >* concOut(); - static const Cinfo* initCinfo(); - private: - double thickness_; - double diameter_; - double length_; + static const Cinfo* initCinfo(); +private: + double thickness_; + double diameter_; + double length_; }; diff --git a/python/moose/neuroml2/reader.py b/python/moose/neuroml2/reader.py index 77d85e2634..7508b6f79e 100644 --- a/python/moose/neuroml2/reader.py +++ b/python/moose/neuroml2/reader.py @@ -12,7 +12,6 @@ # For update/log, please see git-blame documentation or browse the github # repo https://github.com/BhallaLab/moose-core -import os import math import numpy as np from moose.neuroml2.hhfit import exponential2 @@ -23,12 +22,13 @@ import logging logger_ = logging.getLogger('moose.nml2') +logger_.setLevel(logging.INFO) try: - import neuroml as nml import neuroml.loaders as loaders from pyneuroml import pynml -except: +except Exception as e: + print(e) print( "********************************************************************") print("* ") @@ -42,7 +42,71 @@ print( "********************************************************************") -# Utility functions + +# these gates are available in moose. These are prefixed by 'gate' +_validMooseHHGateIds = ['X', 'Y', 'Z'] + +def _unique(ls): + res = [] + for l in ls: + if l not in res: + res.append(l) + return res + + +def _whichGate(chan): + global _validMooseHHGateIds + c = chan.name[-1] + assert c in _validMooseHHGateIds + return c + +def _pairNmlGateWithMooseGates(mGates, nmlGates): + """Return moose gate id from nml.HHGate + """ + global _validMooseHHGateIds + # deep copy + mooseGatesMap = {_whichGate(x) : x for x in mGates} + availableMooseGates = _validMooseHHGateIds[:] + mapping = {} + for nmlGate in nmlGates: + if nmlGate is None: + continue + if hasattr(nmlGate, 'id') and nmlGate.id: + mapping[nmlGate.id.upper()] = nmlGate + availableMooseGates.remove(nmlGate.id.upper()) + else: + mapping[availableMooseGates.pop(0)] = nmlGate + + # Now replace 'X', 'Y', 'Z' with moose gates. + return [(mooseGatesMap[x], mapping[x]) for x in mapping] + +def _isConcDep(ct): + """_isConcDep + Check if componet is dependant on concentration. Most HHGates are + dependant on voltage. + + :param ct: ComponentType + :type ct: nml.ComponentType + + :return: True if Component is depenant on conc, False otherwise. + """ + if not hasattr(ct, 'extends'): + return False + if 'ConcDep' in ct.extends: + return True + return False + + +def _findCaConc(): + """_findCaConc + Find a suitable CaConc for computing HHGate tables. + This is a hack, though it is likely to work in most cases. + """ + caConcs = moose.wildcardFind('/library/##[TYPE=CaConc]') + assert len(caConcs) == 1, "No moose.CaConc found. Currently moose \ + supports HHChannel which depends only on moose.CaConc ." + + return caConcs[0] def sarea(comp): @@ -161,6 +225,8 @@ def __init__(self, verbose=False): self.seg_id_to_comp_name = {} self.paths_to_chan_elements = {} + # Just in case. + self._variables = {} def read(self, filename, symmetric=True): self.doc = loaders.read_neuroml2_file(filename, @@ -425,8 +491,9 @@ def isPassiveChan(self, chan): 'HHExpLinearRate': linoid2 } - def calculateRateFn(self, ratefn, vmin, vmax, tablen=3000, vShift='0mV'): + def calculateRateFn(self, ratefn, mgate, vmin, vmax, tablen=3000, vShift='0mV'): """Returns A / B table from ngate.""" + tab = np.linspace(vmin, vmax, tablen) if self._is_standard_nml_rate(ratefn): midpoint, rate, scale = map( @@ -437,16 +504,87 @@ def calculateRateFn(self, ratefn, vmin, vmax, tablen=3000, vShift='0mV'): if ratefn.type != ct.name: continue logger_.info("Using %s to evaluate rate" % ct.name) + if not _isConcDep(ct): + return self._computeRateFn(ct, tab) + else: + ca = _findCaConc() + if _whichGate(mgate) != 'Z': + raise RuntimeWarning("Concentration dependant gate " + " should use gateZ of moose.HHChannel. " + " If you know what you are doing, ignore this " + " warning. " + ) + return self._computeRateFnCa(ca, ct, tab, vShift=vShift) + + def _computeRateFnCa(self, ca, ct, tab, vShift): + rate = [] + for v in tab: + req_vars = { + ca.name: '%sV' % v, + 'vShift': vShift, + 'temperature': self._getTemperature() + } + req_vars.update(self._variables) + vals = pynml.evaluate_component(ct, req_variables=req_vars) + '''print(vals)''' + if 'x' in vals: + rate.append(vals['x']) + if 't' in vals: + rate.append(vals['t']) + if 'r' in vals: + rate.append(vals['r']) + return np.array(rate) + + def _computeRateFn(self, ct, tab, vShift): + rate = [] + for v in tab: + req_vars = { + 'v': '%sV' % v, + 'vShift': vShift, + 'temperature': self._getTemperature() + } + req_vars.update(self._variables) + vals = pynml.evaluate_component(ct, req_variables=req_vars) + '''print(vals)''' + if 'x' in vals: + rate.append(vals['x']) + if 't' in vals: + rate.append(vals['t']) + if 'r' in vals: + rate.append(vals['r']) + return np.array(rate) + + def calculateRateFnCaDep(self, + ratefn, + ca, + camin, + camax, + tablen=3000, + vShift='0mV'): + """Returns A / B table from ngate. + + This function compute rate which depends on ca. + This must go into Z gate. + """ + tab = np.linspace(camin, camax, tablen) + if self._is_standard_nml_rate(ratefn): + midpoint, rate, scale = map( + SI, (ratefn.midpoint, ratefn.rate, ratefn.scale)) + return self.rate_fn_map[ratefn.type](tab, rate, scale, midpoint) + + for ct in self.doc.ComponentType: + if ratefn.type != ct.name: + continue + logger_.info("Using %s to evaluate rate (caConc dependant)" % + ct.name) rate = [] for v in tab: - vars = { - 'v': '%sV' % v, + req_vars = { + ca.name: '%sV' % v, 'vShift': vShift, 'temperature': self._getTemperature() - } - for k in self.nml_concs_to_moose: - vars.update({k: self.nml_concs_to_moose[k].conc}) - vals = pynml.evaluate_component(ct, req_variables=vars) + } + vals = pynml.evaluate_component(ct, req_variables=req_vars) '''print(vals)''' if 'x' in vals: rate.append(vals['x']) @@ -553,90 +691,109 @@ def _is_standard_nml_rate(self, rate): rate.type=='HHSigmoidVariable' def createHHChannel(self, chan, vmin=-150e-3, vmax=100e-3, vdivs=5000): - mchan = moose.HHChannel('%s/%s' % (self.lib.path, chan.id)) - mgates = map(moose.element, (mchan.gateX, mchan.gateY, mchan.gateZ)) - assert (len(chan.gate_hh_rates) <= 3 - ) # We handle only up to 3 gates in HHCHannel + path = '%s/%s' % (self.lib.path, chan.id) + if moose.exists(path): + mchan = moose.element(path) + else: + mchan = moose.HHChannel(path) + mgates = [ + moose.element(g) for g in [mchan.gateX, mchan.gateY, mchan.gateZ] + ] + + # We handle only up to 3 gates in HHCHannel + assert len(chan.gate_hh_rates) <= 3, "No more than 3 gates" if self.verbose: print('== Creating channel: %s (%s) -> %s (%s)' % (chan.id, chan.gate_hh_rates, mchan, mgates)) + all_gates = chan.gates + chan.gate_hh_rates - for ngate, mgate in zip(all_gates, mgates): - if mgate.name.endswith('X'): - mchan.Xpower = ngate.instances - elif mgate.name.endswith('Y'): - mchan.Ypower = ngate.instances - elif mgate.name.endswith('Z'): - mchan.Zpower = ngate.instance - mgate.min = vmin - mgate.max = vmax - mgate.divs = vdivs - - # I saw only examples of GateHHRates in - # HH-channels, the meaning of forwardRate and - # reverseRate and steadyState are not clear in the - # classes GateHHRatesInf, GateHHRatesTau and in - # FateHHTauInf the meaning of timeCourse and - # steady state is not obvious. Is the last one - # refering to tau_inf and m_inf?? - fwd = ngate.forward_rate - rev = ngate.reverse_rate - - self.paths_to_chan_elements[ - '%s/%s' % - (chan.id, ngate.id)] = '%s/%s' % (chan.id, mgate.name) - - q10_scale = 1 - if ngate.q10_settings: - if ngate.q10_settings.type == 'q10Fixed': - q10_scale = float(ngate.q10_settings.fixed_q10) - elif ngate.q10_settings.type == 'q10ExpTemp': - q10_scale = math.pow( - float(ngate.q10_settings.q10_factor), - (self._getTemperature() - - SI(ngate.q10_settings.experimental_temp)) / 10) - #print('Q10: %s; %s; %s; %s'%(ngate.q10_settings.q10_factor, self._getTemperature(),SI(ngate.q10_settings.experimental_temp),q10_scale)) - else: - raise Exception( - 'Unknown Q10 scaling type %s: %s' % - (ngate.q10_settings.type, ngate.q10_settings)) - if self.verbose: - print( - ' === Gate: %s; %s; %s; %s; %s; scale=%s' % - (ngate.id, mgate.path, mchan.Xpower, fwd, rev, q10_scale)) - - if (fwd is not None) and (rev is not None): - alpha = self.calculateRateFn(fwd, vmin, vmax, vdivs) - beta = self.calculateRateFn(rev, vmin, vmax, vdivs) - mgate.tableA = q10_scale * (alpha) - mgate.tableB = q10_scale * (alpha + beta) - - # Assuming the meaning of the elements in GateHHTauInf ... - if hasattr(ngate,'time_course') and hasattr(ngate,'steady_state') \ - and (ngate.time_course is not None) and (ngate.steady_state is not None): - tau = ngate.time_course - inf = ngate.steady_state - tau = self.calculateRateFn(tau, vmin, vmax, vdivs) + # If user set bnml channels' id to 'x', 'y' or 'z' then pair this gate + # with moose.HHChannel's gateX, gateY, gateZ respectively. Else pair + # them with gateX, gateY, gateZ acording to list order. + for mgate, ngate in _pairNmlGateWithMooseGates(mgates, all_gates): + self._addGateToHHChannel(chan, mchan, mgate, ngate, vmin, vmax, + vdivs) + logger_.debug('== Created', mchan.path, 'for', chan.id) + return mchan + + def _addGateToHHChannel(self, chan, mchan, mgate, ngate, vmin, vmax, + vdivs): + """Add gateX, gateY, gateZ etc to moose.HHChannel (mchan). + + Each gate can be voltage dependant and/or concentration dependant. + Only caConc dependant channels are supported. + """ + + # set mgate.Xpower, .Ypower etc. + setattr(mchan, _whichGate(mgate) + 'power', ngate.instances) + + mgate.min = vmin + mgate.max = vmax + mgate.divs = vdivs + + # Note by Padraig: + # --------------- + # I saw only examples of GateHHRates in HH-channels, the meaning of + # forwardRate and reverseRate and steadyState are not clear in the + # classes GateHHRatesInf, GateHHRatesTau and in FateHHTauInf the + # meaning of timeCourse and steady state is not obvious. Is the last + # one # refering to tau_inf and m_inf?? + fwd = ngate.forward_rate + rev = ngate.reverse_rate + + self.paths_to_chan_elements['%s/%s'%(chan.id, ngate.id)] = \ + '%s/%s' % (chan.id, mgate.name) + + q10_scale = 1 + if ngate.q10_settings: + if ngate.q10_settings.type == 'q10Fixed': + q10_scale = float(ngate.q10_settings.fixed_q10) + elif ngate.q10_settings.type == 'q10ExpTemp': + q10_scale = math.pow( + float(ngate.q10_settings.q10_factor), + (self._getTemperature() - + SI(ngate.q10_settings.experimental_temp)) / 10) + logger_.debug( + 'Q10: %s; %s; %s; %s' % + (ngate.q10_settings.q10_factor, self._getTemperature(), + SI(ngate.q10_settings.experimental_temp), q10_scale)) + else: + raise Exception('Unknown Q10 scaling type %s: %s' % + (ngate.q10_settings.type, ngate.q10_settings)) + logger_.info(' === Gate: %s; %s; %s; %s; %s; scale=%s' % + (ngate.id, mgate.path, mchan.Xpower, fwd, rev, q10_scale)) + + if (fwd is not None) and (rev is not None): + # Note: MOOSE HHGate are either voltage of concentration + # dependant. Here we figure out if nml description of gate is + # concentration dependant or not. + alpha = self.calculateRateFn(fwd, mgate, vmin, vmax, vdivs) + beta = self.calculateRateFn(rev, mgate, vmin, vmax, vdivs) + + mgate.tableA = q10_scale * (alpha) + mgate.tableB = q10_scale * (alpha + beta) + + # Assuming the meaning of the elements in GateHHTauInf ... + if hasattr(ngate,'time_course') and hasattr(ngate,'steady_state') \ + and (ngate.time_course is not None) and (ngate.steady_state is not None): + tau = ngate.time_course + inf = ngate.steady_state + tau = self.calculateRateFn(tau, mgate, vmin, vmax, vdivs) + inf = self.calculateRateFn(inf, mgate, vmin, vmax, vdivs) + mgate.tableA = q10_scale * (inf / tau) + mgate.tableB = q10_scale * (1 / tau) + + if hasattr(ngate, 'steady_state') and (ngate.time_course is None) \ + and (ngate.steady_state is not None): + inf = ngate.steady_state + tau = 1 / (alpha + beta) + if (inf is not None): inf = self.calculateRateFn(inf, vmin, vmax, vdivs) mgate.tableA = q10_scale * (inf / tau) mgate.tableB = q10_scale * (1 / tau) - if hasattr(ngate, - 'steady_state') and (ngate.time_course is None) and ( - ngate.steady_state is not None): - inf = ngate.steady_state - tau = 1 / (alpha + beta) - if (inf is not None): - inf = self.calculateRateFn(inf, vmin, vmax, vdivs) - mgate.tableA = q10_scale * (inf / tau) - mgate.tableB = q10_scale * (1 / tau) - - if self.verbose: - print(self.filename, '== Created', mchan.path, 'for', chan.id) - return mchan - def createPassiveChannel(self, chan): mchan = moose.Leakage('%s/%s' % (self.lib.path, chan.id)) if self.verbose: @@ -666,7 +823,7 @@ def importIonChannels(self, doc, vmin=-150e-3, vmax=100e-3, vdivs=5000): else: mchan = self.createHHChannel(chan) - assert chan.id + assert chan.id, "Empty id is not allowed" self.id_to_ionChannel[chan.id] = chan self.nml_chans_to_moose[chan.id] = mchan self.proto_chans[chan.id] = mchan @@ -681,7 +838,7 @@ def importConcentrationModels(self, doc): def createDecayingPoolConcentrationModel(self, concModel): """Create prototype for concentration model""" - assert concModel.id + assert concModel.id, "Empty id is not allowed" name = concModel.id if hasattr(concModel, 'name') and concModel.name is not None: name = concModel.name diff --git a/tests/support/CaPool.nml b/tests/support/CaPool.nml new file mode 100644 index 0000000000..2358c75b5d --- /dev/null +++ b/tests/support/CaPool.nml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/tests/support/MScellupdated_primDend.nml b/tests/support/MScellupdated_primDend.nml new file mode 100644 index 0000000000..26d9c80727 --- /dev/null +++ b/tests/support/MScellupdated_primDend.nml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/SKchannel2.nml b/tests/support/SKchannel2.nml new file mode 100644 index 0000000000..9c2605d67a --- /dev/null +++ b/tests/support/SKchannel2.nml @@ -0,0 +1,107 @@ + + + + + + + Small-conductance, Ca2+ activated K+ current + +Comment from original mod file: +: SK-type calcium-activated potassium current +: Reference : Kohler et al. 1996 + + + + + + + + Models of Neocortical Layer 5b Pyramidal Cells Capturing a Wide Range of Dendritic and Perisomatic Active Properties, + Etay Hay, Sean Hill, Felix Schürmann, Henry Markram and Idan Segev, PLoS Comp Biol 2011 + + + + + + + + K channels + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/pas.channel.nml b/tests/support/pas.channel.nml new file mode 100644 index 0000000000..16ed312a3e --- /dev/null +++ b/tests/support/pas.channel.nml @@ -0,0 +1,12 @@ + + + + NeuroML file containing a single Channel description + + + + Simple example of a leak/passive conductance. + + + + diff --git a/tests/support/test_nml2.py b/tests/support/test_nml2.py new file mode 100644 index 0000000000..0d9a58076a --- /dev/null +++ b/tests/support/test_nml2.py @@ -0,0 +1,160 @@ +# Author: @jkopsick (github) https://github.com/jkopsick +# +# Turned into a test case by Dilawar Singh + +import os +import matplotlib +matplotlib.use('Agg') +import matplotlib.pyplot as plt + +import moose +print( "[INFO ] Using moose from %s" % moose.__file__ ) +import numpy as np +import datetime + +def plot_gate_params(chan,plotpow, VMIN=-0.1, VMAX=0.05, CAMIN=0, CAMAX=1): + """Plot the gate parameters like m and h of the channel.""" + if chan.className == 'HHChannel': + print( "[INFO ] Testing moose.HHChannel with CaConc" ) + cols=1 + #n=range(0,2,1) + if chan.Zpower!=0 and (chan.Xpower!=0 or chan.Ypower!=0) and chan.useConcentration == True: + fig,axes=plt.subplots(3,cols,sharex=False) + axes[1].set_xlabel('voltage') + axes[2].set_xlabel('Calcium') + else: + fig,axes=plt.subplots(2,cols,sharex=True) + axes[1].set_xlabel('voltage') + plt.suptitle(chan.name) + + if chan.Xpower > 0: + assert False, "Should load into gateZ" + gate=moose.element(chan.path + '/gateX') + ma = gate.tableA + mb = gate.tableB + varray = np.linspace(gate.min, gate.max, len(ma)) + axes[0].plot(varray, 1e3 / mb, label='Xtau ' + chan.name) + labelpow = '(Xinf)**{}'.format(chan.Xpower) + infpow = (ma / mb) ** chan.Xpower + label = 'Xinf' + inf = ma / mb + axes[1].plot(varray, inf, label=label) + axes[1].plot(varray, infpow, label=labelpow) + axes[1].axis([gate.min, gate.max, 0, 1]) + + if chan.Ypower > 0: + assert False, "Should load into gateZ" + gate=moose.element(chan.path + '/gateY') + ha = gate.tableA + hb = gate.tableB + varray = np.linspace(gate.min, gate.max, len(ha)) + axes[0].plot(varray, 1e3 / hb, label='Ytau ' + chan.name) + axes[1].plot(varray, ha / hb, label='Yinf ' + chan.name) + axes[1].axis([gate.min, gate.max, 0, 1]) + + if chan.Zpower!=0: + gate=moose.element(chan.path + '/gateZ') + gate.min = CAMIN + gate.max = CAMAX + za = gate.tableA + zb = gate.tableB + Z = za/zb + + # FIXME: These values may not be correct. Once verified, remove + # this comment or fix the test. + assert np.isclose(Z.mean(), 0.9954291788156363) + assert np.isclose(Z.std(), 0.06818901739648629) + + xarray=np.linspace(gate.min,gate.max,len(za)) + if (chan.Xpower==0 and chan.Ypower==0) or chan.useConcentration == False: + axes[0].plot(xarray,1e3/zb, label='ztau ' + chan.name) + axes[1].plot(xarray, za / zb, label='zinf' + chan.name) + if chan.useConcentration == True: + axes[1].set_xlabel('Calcium') + else: + axes[2].set_xscale("log") + axes[2].set_ylabel('ss, tau (s)') + axes[2].plot(xarray,1/zb,label='ztau ' + chan.name) + axes[2].plot(xarray, za / zb, label='zinf ' + chan.name) + axes[2].legend(loc='best', fontsize=8) + axes[0].set_ylabel('tau, ms') + axes[1].set_ylabel('steady state') + axes[0].legend(loc='best', fontsize=8) + axes[1].legend(loc='best', fontsize=8) + else: #Must be two-D tab channel + plt.figure() + + ma = moose.element(chan.path + '/gateX').tableA + mb = moose.element(chan.path + '/gateX').tableB + ma = np.array(ma) + mb = np.array(mb) + + plt.subplot(211) + + plt.title(chan.name+'/gateX top: tau (ms), bottom: ss') + plt.imshow(1e3/mb,extent=[CAMIN,CAMAX,VMIN,VMAX],aspect='auto',origin='lower') + plt.colorbar() + + plt.subplot(212) + if plotpow: + inf = (ma/mb)**chan.Xpower + else: + inf = ma/mb + + plt.imshow(inf,extent=[CAMIN,CAMAX,VMIN,VMAX],aspect='auto',origin='lower') + plt.xlabel('Ca [mM]') + plt.ylabel('Vm [V]') + plt.colorbar() + if chan.Ypower > 0: + ha = moose.element(chan.path + '/gateY').tableA + hb = moose.element(chan.path + '/gateY').tableB + ha = np.array(ha) + hb = np.array(hb) + + plt.figure() + plt.subplot(211) + plt.suptitle(chan.name+'/gateY tau') + plt.imshow(1e3/hb,extent=[CAMIN,CAMAX,VMIN,VMAX],aspect='auto') + + plt.colorbar() + plt.subplot(212) + if plotpow: + inf = (ha/hb)**chan.Ypower + else: + inf = ha/hb + plt.imshow(inf,extent=[CAMIN,CAMAX,VMIN,VMAX],aspect='auto') + plt.xlabel('Ca [nM]') + plt.ylabel('Vm [V]') + plt.colorbar() + return + +def test_nml2_jkopsick(): + # Read the NML model into MOOSE + sdir = os.path.dirname(os.path.realpath(__file__)) + filename = os.path.join(sdir, 'MScellupdated_primDend.nml') + moose.mooseReadNML2(filename, verbose = 1) + + # Define the variables needed to view the underlying curves for the channel kinetics + plot_powers = True + VMIN = -120e-3 + VMAX = 50e-3 + CAMIN = 0.01e-3 + CAMAX = 40e-3 + + # Graph the channel kinetics for the SKCa channel -- the plot doesn't + # currently show up due to the error + # in copying the CaPool mechanism, but works if you run it manually after + # the code completes. + libchan=moose.element('/library'+ '/' + 'SKCa') + plot_gate_params(libchan, plot_powers, VMIN, VMAX, CAMIN, CAMAX) + + stamp = datetime.datetime.now().isoformat() + plt.suptitle(stamp, fontsize=6) + plt.tight_layout() + plt.savefig(__file__ + ".png") + +def main(): + test_nml2_jkopsick() + +if __name__ == '__main__': + main() From d5760490436d9cd0e7197d781ace49f6a4b710bb Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Fri, 13 Mar 2020 12:32:06 +0530 Subject: [PATCH 12/16] - If channels are named 'm', 'n', load them into 'X', 'Y', 'Z' in the order of appearance. - Cleanup in tests. --- python/moose/__init__.py | 3 + python/moose/model_utils.py | 90 ++++++++++-------- python/moose/neuroml2/__main__.py | 57 +++++++++++ python/moose/neuroml2/reader.py | 35 ++++--- tests/support/CaPool.nml | 10 -- tests/support/MScellupdated_primDend.nml | 113 ---------------------- tests/support/SKchannel2.nml | 107 --------------------- tests/support/pas.channel.nml | 12 --- tests/support/test_files | 1 - tests/support/test_neuroml2.py | 56 ----------- tests/support/test_nml2.py | 116 ++++++++++++++++------- 11 files changed, 218 insertions(+), 382 deletions(-) create mode 100644 python/moose/neuroml2/__main__.py delete mode 100644 tests/support/CaPool.nml delete mode 100644 tests/support/MScellupdated_primDend.nml delete mode 100644 tests/support/SKchannel2.nml delete mode 100644 tests/support/pas.channel.nml delete mode 120000 tests/support/test_files delete mode 100644 tests/support/test_neuroml2.py diff --git a/python/moose/__init__.py b/python/moose/__init__.py index 8749afda5f..ae08cb5cc0 100644 --- a/python/moose/__init__.py +++ b/python/moose/__init__.py @@ -1,6 +1,9 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function +import logging +logging.basicConfig(format='%(asctime)-15s %(name)-12s %(levelname)-8s %(message)s') + # Bring everything from c++ module to global namespace. from moose._moose import * diff --git a/python/moose/model_utils.py b/python/moose/model_utils.py index d4b7e21f23..ba62c29347 100644 --- a/python/moose/model_utils.py +++ b/python/moose/model_utils.py @@ -26,13 +26,12 @@ import moose.neuroml2 as _neuroml2 except Exception as e: nml2Import_ = False - nml2ImportError_ = ' '.join( [ - "NML2 support is disabled because `libneuroml` and " - , "`pyneuroml` modules are not found.\n" - , " $ pip install pyneuroml libneuroml \n" - , " should fix it." - , " Actual error: %s " % e ] - ) + nml2ImportError_ = ' '.join([ + "NML2 support is disabled because `libneuroml` and ", + "`pyneuroml` modules are not found.\n", + " $ pip install pyneuroml libneuroml \n", " should fix it.", + " Actual error: %s " % e + ]) chemImport_, chemError_ = True, '' try: @@ -55,6 +54,7 @@ mergechemImport_ = False mergechemError_ = '%s' % e + # SBML related functions. def mooseReadSBML(filepath, loadpath, solver='ee', validate="on"): """Load SBML model. @@ -73,24 +73,30 @@ def mooseReadSBML(filepath, loadpath, solver='ee', validate="on"): """ global sbmlImport_ if sbmlImport_: - modelpath = _readSBML.mooseReadSBML(filepath, loadpath, solver, validate) + modelpath = _readSBML.mooseReadSBML(filepath, loadpath, solver, + validate) sc = solver.lower() - if sc in ["gssa","gillespie","stochastic","gsolve"]: + if sc in ["gssa", "gillespie", "stochastic", "gsolve"]: method = "gssa" - elif sc in ["gsl","runge kutta","deterministic","ksolve","rungekutta","rk5","rkf","rk"]: + elif sc in [ + "gsl", "runge kutta", "deterministic", "ksolve", "rungekutta", + "rk5", "rkf", "rk" + ]: method = "gsl" - elif sc in ["exponential euler","exponentialeuler","neutral", "ee"]: + elif sc in ["exponential euler", "exponentialeuler", "neutral", "ee"]: method = "ee" else: method = "ee" if method != 'ee': - _chemUtil.add_Delete_ChemicalSolver.mooseAddChemSolver(modelpath[0].path, method) + _chemUtil.add_Delete_ChemicalSolver.mooseAddChemSolver( + modelpath[0].path, method) return modelpath else: - logger_.error( sbmlError_ ) + logger_.error(sbmlError_) return False + def mooseWriteSBML(modelpath, filepath, sceneitems={}): """mooseWriteSBML: Writes loaded model under modelpath to a file in SBML format. @@ -112,7 +118,7 @@ def mooseWriteSBML(modelpath, filepath, sceneitems={}): if sbmlImport_: return _writeSBML.mooseWriteSBML(modelpath, filepath, sceneitems) else: - logger_.error( sbmlError_ ) + logger_.error(sbmlError_) return False @@ -128,10 +134,10 @@ def mooseWriteKkit(modelpath, filepath, sceneitems={}): """ global kkitImport_, kkitImport_err_ if not kkitImport_: - print( '[WARN] Could not import module to enable this function' ) - print( '\tError was %s' % kkitImport_error_ ) + print('[WARN] Could not import module to enable this function') + print('\tError was %s' % kkitImport_error_) return False - return _writeKkit.mooseWriteKkit(modelpath, filepath,sceneitems) + return _writeKkit.mooseWriteKkit(modelpath, filepath, sceneitems) def mooseDeleteChemSolver(modelpath): @@ -145,9 +151,10 @@ def mooseDeleteChemSolver(modelpath): to simulate else default is Exponential Euler (ee) """ if chemImport_: - return _chemUtil.add_Delete_ChemicalSolver.mooseDeleteChemSolver(modelpath) + return _chemUtil.add_Delete_ChemicalSolver.mooseDeleteChemSolver( + modelpath) else: - print( chemError_ ) + print(chemError_) return False @@ -165,37 +172,40 @@ def mooseAddChemSolver(modelpath, solver): Runge Kutta ("gsl") etc. Link to documentation? """ if chemImport_: - chemError_ = _chemUtil.add_Delete_ChemicalSolver.mooseAddChemSolver(modelpath, solver) + chemError_ = _chemUtil.add_Delete_ChemicalSolver.mooseAddChemSolver( + modelpath, solver) return chemError_ else: - print( chemError_ ) + print(chemError_) return False + def mergeChemModel(src, des): """mergeChemModel: Merges two chemical model. File or filepath can be passed source is merged to destination """ #global mergechemImport_ if mergechemImport_: - return _chemMerge.merge.mergeChemModel(src,des) + return _chemMerge.merge.mergeChemModel(src, des) else: return False + # NML2 reader and writer function. -def mooseReadNML2( modelpath, verbose = False ): +def mooseReadNML2(modelpath, verbose=False): """Read NeuroML model (version 2) and return reader object. """ global nml2Import_ if not nml2Import_: - mu.warn( nml2ImportError_ ) - raise RuntimeError( "Could not load NML2 support." ) + raise RuntimeError(nml2ImportError_) - reader = _neuroml2.NML2Reader( verbose = verbose ) - reader.read( modelpath ) + reader = _neuroml2.NML2Reader(verbose=verbose) + reader.read(modelpath) return reader -def mooseWriteNML2( outfile ): - raise NotImplementedError( "Writing to NML2 is not supported yet" ) + +def mooseWriteNML2(outfile): + raise NotImplementedError("Writing to NML2 is not supported yet") def loadModel(filename, modelpath, solverclass="gsl"): @@ -217,32 +227,36 @@ def loadModel(filename, modelpath, solverclass="gsl"): moose.element if succcessful else None. """ - if not os.path.isfile( os.path.realpath(filename) ): - mu.warn( "Model file '%s' does not exists or is not readable." % filename ) + if not os.path.isfile(os.path.realpath(filename)): + mu.warn("Model file '%s' does not exists or is not readable." % + filename) return None extension = os.path.splitext(filename)[1] if extension in [".swc", ".p"]: - return _moose.loadModelInternal(filename, modelpath, "Neutral" ) + return _moose.loadModelInternal(filename, modelpath, "Neutral") if extension in [".g", ".cspace"]: # only if genesis or cspace file and method != ee then only # mooseAddChemSolver is called. ret = _moose.loadModelInternal(filename, modelpath, "ee") sc = solverclass.lower() - if sc in ["gssa","gillespie","stochastic","gsolve"]: + if sc in ["gssa", "gillespie", "stochastic", "gsolve"]: method = "gssa" - elif sc in ["gsl","runge kutta","deterministic","ksolve","rungekutta","rk5","rkf","rk"]: + elif sc in [ + "gsl", "runge kutta", "deterministic", "ksolve", "rungekutta", + "rk5", "rkf", "rk" + ]: method = "gsl" - elif sc in ["exponential euler","exponentialeuler","neutral"]: + elif sc in ["exponential euler", "exponentialeuler", "neutral"]: method = "ee" else: method = "ee" if method != 'ee': - _chemUtil.add_Delete_ChemicalSolver.mooseAddChemSolver(modelpath, method) + _chemUtil.add_Delete_ChemicalSolver.mooseAddChemSolver( + modelpath, method) return ret else: - logger_.error( "Unknown model extenstion '%s'" % extension) + logger_.error("Unknown model extenstion '%s'" % extension) return None - diff --git a/python/moose/neuroml2/__main__.py b/python/moose/neuroml2/__main__.py new file mode 100644 index 0000000000..daafc2312b --- /dev/null +++ b/python/moose/neuroml2/__main__.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +# WARNING: This is for debuging purpose and should not be used in production. +# Quickly load neuroml2 into MOOSE. + + +__author__ = "Dilawar Singh" +__copyright__ = "Copyright 2019-, Dilawar Singh" +__maintainer__ = "Dilawar Singh" +__email__ = "dilawars@ncbs.res.in" + +import moose + +import logging +logger_ = logging.getLogger('moose.nml2') + +def _addStreamer(): + logger_.warning('TODO. Add streamer.') + + +def main(**kwargs): + logger_.info("Reading file %s" % kwargs['nml2file']) + moose.mooseReadNML2(kwargs['nml2file']) + + _addStreamer() + + moose.reinit() + simtime = kwargs['runtime'] + moose.start(simtime) + +if __name__ == '__main__': + import argparse + # Argument parser. + description = '''Load neuroml2 model into moose.''' + parser = argparse.ArgumentParser(description=description) + parser.add_argument('nml2file', help = 'Neuroml2 file.' + , metavar="" + ) + parser.add_argument('--output', '-o' + , required = False + , help = 'Output file' + ) + parser.add_argument('--runtime', '-T' + , required = False, default = 1.0, type=float + , help = 'Simulation time (sec)' + ) + + parser.add_argument('--debug', '-d' + , required = False + , default = 0 + , type = int + , help = 'Enable debug mode. Default 0, debug level' + ) + class Args: pass + args = Args() + parser.parse_args(namespace=args) + main(**vars(args)) diff --git a/python/moose/neuroml2/reader.py b/python/moose/neuroml2/reader.py index 7508b6f79e..6f3b7b30ca 100644 --- a/python/moose/neuroml2/reader.py +++ b/python/moose/neuroml2/reader.py @@ -42,9 +42,9 @@ print( "********************************************************************") +# these are the gates available. These are prefixed by 'gate' in C++ codebase. +_validMooseHHGateIds = ['X', 'Y', 'Z'] -# these gates are available in moose. These are prefixed by 'gate' -_validMooseHHGateIds = ['X', 'Y', 'Z'] def _unique(ls): res = [] @@ -60,18 +60,20 @@ def _whichGate(chan): assert c in _validMooseHHGateIds return c + def _pairNmlGateWithMooseGates(mGates, nmlGates): """Return moose gate id from nml.HHGate """ global _validMooseHHGateIds # deep copy - mooseGatesMap = {_whichGate(x) : x for x in mGates} + mooseGatesMap = {_whichGate(x): x for x in mGates} availableMooseGates = _validMooseHHGateIds[:] mapping = {} - for nmlGate in nmlGates: + for nmlGate in nmlGates: if nmlGate is None: continue - if hasattr(nmlGate, 'id') and nmlGate.id: + if hasattr(nmlGate, 'id') and nmlGate.id \ + and nmlGate.id.upper() in availableMooseGates: mapping[nmlGate.id.upper()] = nmlGate availableMooseGates.remove(nmlGate.id.upper()) else: @@ -80,6 +82,7 @@ def _pairNmlGateWithMooseGates(mGates, nmlGates): # Now replace 'X', 'Y', 'Z' with moose gates. return [(mooseGatesMap[x], mapping[x]) for x in mapping] + def _isConcDep(ct): """_isConcDep Check if componet is dependant on concentration. Most HHGates are @@ -491,7 +494,13 @@ def isPassiveChan(self, chan): 'HHExpLinearRate': linoid2 } - def calculateRateFn(self, ratefn, mgate, vmin, vmax, tablen=3000, vShift='0mV'): + def calculateRateFn(self, + ratefn, + mgate, + vmin, + vmax, + tablen=3000, + vShift='0mV'): """Returns A / B table from ngate.""" tab = np.linspace(vmin, vmax, tablen) @@ -509,11 +518,11 @@ def calculateRateFn(self, ratefn, mgate, vmin, vmax, tablen=3000, vShift='0mV'): else: ca = _findCaConc() if _whichGate(mgate) != 'Z': - raise RuntimeWarning("Concentration dependant gate " - " should use gateZ of moose.HHChannel. " - " If you know what you are doing, ignore this " - " warning. " - ) + raise RuntimeWarning( + "Concentration dependant gate " + " should use gateZ of moose.HHChannel. " + " If you know what you are doing, ignore this " + " warning. ") return self._computeRateFnCa(ca, ct, tab, vShift=vShift) def _computeRateFnCa(self, ca, ct, tab, vShift): @@ -695,7 +704,7 @@ def createHHChannel(self, chan, vmin=-150e-3, vmax=100e-3, vdivs=5000): if moose.exists(path): mchan = moose.element(path) else: - mchan = moose.HHChannel(path) + mchan = moose.HHChannel(path) mgates = [ moose.element(g) for g in [mchan.gateX, mchan.gateY, mchan.gateZ] ] @@ -733,7 +742,7 @@ def _addGateToHHChannel(self, chan, mchan, mgate, ngate, vmin, vmax, mgate.max = vmax mgate.divs = vdivs - # Note by Padraig: + # Note by Padraig: # --------------- # I saw only examples of GateHHRates in HH-channels, the meaning of # forwardRate and reverseRate and steadyState are not clear in the diff --git a/tests/support/CaPool.nml b/tests/support/CaPool.nml deleted file mode 100644 index 2358c75b5d..0000000000 --- a/tests/support/CaPool.nml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/tests/support/MScellupdated_primDend.nml b/tests/support/MScellupdated_primDend.nml deleted file mode 100644 index 26d9c80727..0000000000 --- a/tests/support/MScellupdated_primDend.nml +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/support/SKchannel2.nml b/tests/support/SKchannel2.nml deleted file mode 100644 index 9c2605d67a..0000000000 --- a/tests/support/SKchannel2.nml +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - - Small-conductance, Ca2+ activated K+ current - -Comment from original mod file: -: SK-type calcium-activated potassium current -: Reference : Kohler et al. 1996 - - - - - - - - Models of Neocortical Layer 5b Pyramidal Cells Capturing a Wide Range of Dendritic and Perisomatic Active Properties, - Etay Hay, Sean Hill, Felix Schürmann, Henry Markram and Idan Segev, PLoS Comp Biol 2011 - - - - - - - - K channels - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/support/pas.channel.nml b/tests/support/pas.channel.nml deleted file mode 100644 index 16ed312a3e..0000000000 --- a/tests/support/pas.channel.nml +++ /dev/null @@ -1,12 +0,0 @@ - - - - NeuroML file containing a single Channel description - - - - Simple example of a leak/passive conductance. - - - - diff --git a/tests/support/test_files b/tests/support/test_files deleted file mode 120000 index 6866342fef..0000000000 --- a/tests/support/test_files +++ /dev/null @@ -1 +0,0 @@ -../../python/moose/neuroml2/test_files \ No newline at end of file diff --git a/tests/support/test_neuroml2.py b/tests/support/test_neuroml2.py deleted file mode 100644 index 06275d6226..0000000000 --- a/tests/support/test_neuroml2.py +++ /dev/null @@ -1,56 +0,0 @@ -# -*- coding: utf-8 -*- -# test_neurom2.py, modified from run_cell.py -# Maintainer: P Gleeson, Dilawar Singh -# This test is not robust. -# Code: - -from __future__ import absolute_import, print_function, division - -# check if neuroml working properly. -# NOTE: This script does not work with python3 -# See https://github.com/NeuroML/NeuroML2/issues/116 . If this bug is fixed then -# remove this code block. - -import moose -import moose.utils as mu -import os -import numpy as np - -SCRIPT_DIR = os.path.dirname( os.path.realpath( __file__ ) ) - - -def test_nml2( nogui = True ): - global SCRIPT_DIR - filename = os.path.join(SCRIPT_DIR, 'test_files/passiveCell.nml' ) - mu.info('Loading: %s' % filename ) - nml = moose.mooseReadNML2( filename ) - if not nml: - mu.warn( "Failed to parse NML2 file" ) - return - - assert nml, "Expecting NML2 object" - msoma = nml.getComp(nml.doc.networks[0].populations[0].id,0,0) - data = moose.Neutral('/data') - pg = nml.getInput('pulseGen1') - - inj = moose.Table('%s/pulse' % (data.path)) - moose.connect(inj, 'requestOut', pg, 'getOutputValue') - - vm = moose.Table('%s/Vm' % (data.path)) - moose.connect(vm, 'requestOut', msoma, 'getVm') - - simtime = 150e-3 - moose.reinit() - moose.start(simtime) - print("Finished simulation!") - yvec = vm.vector - injvec = inj.vector * 1e12 - m1, u1 = np.mean( yvec ), np.std( yvec ) - m2, u2 = np.mean( injvec ), np.std( injvec ) - assert np.isclose( m1, -0.0456943 ), m1 - assert np.isclose( u1, 0.0121968 ), u1 - assert np.isclose( m2, 26.64890 ), m2 - assert np.isclose( u2, 37.70607574 ), u2 - -if __name__ == '__main__': - test_nml2() diff --git a/tests/support/test_nml2.py b/tests/support/test_nml2.py index 0d9a58076a..e048575a20 100644 --- a/tests/support/test_nml2.py +++ b/tests/support/test_nml2.py @@ -3,39 +3,45 @@ # Turned into a test case by Dilawar Singh import os -import matplotlib +import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import moose -print( "[INFO ] Using moose from %s" % moose.__file__ ) +import moose.utils as mu + +print("[INFO ] Using moose from %s" % moose.__file__) import numpy as np import datetime -def plot_gate_params(chan,plotpow, VMIN=-0.1, VMAX=0.05, CAMIN=0, CAMAX=1): +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) + + +def plot_gate_params(chan, plotpow, VMIN=-0.1, VMAX=0.05, CAMIN=0, CAMAX=1): """Plot the gate parameters like m and h of the channel.""" if chan.className == 'HHChannel': - print( "[INFO ] Testing moose.HHChannel with CaConc" ) - cols=1 + print("[INFO ] Testing moose.HHChannel with CaConc") + cols = 1 #n=range(0,2,1) - if chan.Zpower!=0 and (chan.Xpower!=0 or chan.Ypower!=0) and chan.useConcentration == True: - fig,axes=plt.subplots(3,cols,sharex=False) + if chan.Zpower != 0 and (chan.Xpower != 0 or chan.Ypower != 0 + ) and chan.useConcentration == True: + fig, axes = plt.subplots(3, cols, sharex=False) axes[1].set_xlabel('voltage') axes[2].set_xlabel('Calcium') else: - fig,axes=plt.subplots(2,cols,sharex=True) + fig, axes = plt.subplots(2, cols, sharex=True) axes[1].set_xlabel('voltage') plt.suptitle(chan.name) if chan.Xpower > 0: assert False, "Should load into gateZ" - gate=moose.element(chan.path + '/gateX') + gate = moose.element(chan.path + '/gateX') ma = gate.tableA mb = gate.tableB varray = np.linspace(gate.min, gate.max, len(ma)) axes[0].plot(varray, 1e3 / mb, label='Xtau ' + chan.name) labelpow = '(Xinf)**{}'.format(chan.Xpower) - infpow = (ma / mb) ** chan.Xpower + infpow = (ma / mb)**chan.Xpower label = 'Xinf' inf = ma / mb axes[1].plot(varray, inf, label=label) @@ -44,7 +50,7 @@ def plot_gate_params(chan,plotpow, VMIN=-0.1, VMAX=0.05, CAMIN=0, CAMAX=1): if chan.Ypower > 0: assert False, "Should load into gateZ" - gate=moose.element(chan.path + '/gateY') + gate = moose.element(chan.path + '/gateY') ha = gate.tableA hb = gate.tableB varray = np.linspace(gate.min, gate.max, len(ha)) @@ -52,29 +58,30 @@ def plot_gate_params(chan,plotpow, VMIN=-0.1, VMAX=0.05, CAMIN=0, CAMAX=1): axes[1].plot(varray, ha / hb, label='Yinf ' + chan.name) axes[1].axis([gate.min, gate.max, 0, 1]) - if chan.Zpower!=0: - gate=moose.element(chan.path + '/gateZ') + if chan.Zpower != 0: + gate = moose.element(chan.path + '/gateZ') gate.min = CAMIN gate.max = CAMAX za = gate.tableA zb = gate.tableB - Z = za/zb + Z = za / zb # FIXME: These values may not be correct. Once verified, remove # this comment or fix the test. assert np.isclose(Z.mean(), 0.9954291788156363) assert np.isclose(Z.std(), 0.06818901739648629) - xarray=np.linspace(gate.min,gate.max,len(za)) - if (chan.Xpower==0 and chan.Ypower==0) or chan.useConcentration == False: - axes[0].plot(xarray,1e3/zb, label='ztau ' + chan.name) + xarray = np.linspace(gate.min, gate.max, len(za)) + if (chan.Xpower == 0 + and chan.Ypower == 0) or chan.useConcentration == False: + axes[0].plot(xarray, 1e3 / zb, label='ztau ' + chan.name) axes[1].plot(xarray, za / zb, label='zinf' + chan.name) if chan.useConcentration == True: axes[1].set_xlabel('Calcium') else: axes[2].set_xscale("log") axes[2].set_ylabel('ss, tau (s)') - axes[2].plot(xarray,1/zb,label='ztau ' + chan.name) + axes[2].plot(xarray, 1 / zb, label='ztau ' + chan.name) axes[2].plot(xarray, za / zb, label='zinf ' + chan.name) axes[2].legend(loc='best', fontsize=8) axes[0].set_ylabel('tau, ms') @@ -91,17 +98,23 @@ def plot_gate_params(chan,plotpow, VMIN=-0.1, VMAX=0.05, CAMIN=0, CAMAX=1): plt.subplot(211) - plt.title(chan.name+'/gateX top: tau (ms), bottom: ss') - plt.imshow(1e3/mb,extent=[CAMIN,CAMAX,VMIN,VMAX],aspect='auto',origin='lower') + plt.title(chan.name + '/gateX top: tau (ms), bottom: ss') + plt.imshow(1e3 / mb, + extent=[CAMIN, CAMAX, VMIN, VMAX], + aspect='auto', + origin='lower') plt.colorbar() plt.subplot(212) if plotpow: - inf = (ma/mb)**chan.Xpower + inf = (ma / mb)**chan.Xpower else: - inf = ma/mb + inf = ma / mb - plt.imshow(inf,extent=[CAMIN,CAMAX,VMIN,VMAX],aspect='auto',origin='lower') + plt.imshow(inf, + extent=[CAMIN, CAMAX, VMIN, VMAX], + aspect='auto', + origin='lower') plt.xlabel('Ca [mM]') plt.ylabel('Vm [V]') plt.colorbar() @@ -113,26 +126,62 @@ def plot_gate_params(chan,plotpow, VMIN=-0.1, VMAX=0.05, CAMIN=0, CAMAX=1): plt.figure() plt.subplot(211) - plt.suptitle(chan.name+'/gateY tau') - plt.imshow(1e3/hb,extent=[CAMIN,CAMAX,VMIN,VMAX],aspect='auto') + plt.suptitle(chan.name + '/gateY tau') + plt.imshow(1e3 / hb, + extent=[CAMIN, CAMAX, VMIN, VMAX], + aspect='auto') plt.colorbar() plt.subplot(212) if plotpow: - inf = (ha/hb)**chan.Ypower + inf = (ha / hb)**chan.Ypower else: - inf = ha/hb - plt.imshow(inf,extent=[CAMIN,CAMAX,VMIN,VMAX],aspect='auto') + inf = ha / hb + plt.imshow(inf, extent=[CAMIN, CAMAX, VMIN, VMAX], aspect='auto') plt.xlabel('Ca [nM]') plt.ylabel('Vm [V]') plt.colorbar() return + +def test_nml2(nogui=True): + global SCRIPT_DIR + filename = os.path.join(SCRIPT_DIR, 'nml_files/passiveCell.nml') + mu.info('Loading: %s' % filename) + nml = moose.mooseReadNML2(filename) + if not nml: + mu.warn("Failed to parse NML2 file") + return + + assert nml, "Expecting NML2 object" + msoma = nml.getComp(nml.doc.networks[0].populations[0].id, 0, 0) + data = moose.Neutral('/data') + pg = nml.getInput('pulseGen1') + + inj = moose.Table('%s/pulse' % (data.path)) + moose.connect(inj, 'requestOut', pg, 'getOutputValue') + + vm = moose.Table('%s/Vm' % (data.path)) + moose.connect(vm, 'requestOut', msoma, 'getVm') + + simtime = 150e-3 + moose.reinit() + moose.start(simtime) + print("Finished simulation!") + yvec = vm.vector + injvec = inj.vector * 1e12 + m1, u1 = np.mean(yvec), np.std(yvec) + m2, u2 = np.mean(injvec), np.std(injvec) + assert np.isclose(m1, -0.0456943), m1 + assert np.isclose(u1, 0.0121968), u1 + assert np.isclose(m2, 26.64890), m2 + assert np.isclose(u2, 37.70607574), u2 + + def test_nml2_jkopsick(): # Read the NML model into MOOSE - sdir = os.path.dirname(os.path.realpath(__file__)) - filename = os.path.join(sdir, 'MScellupdated_primDend.nml') - moose.mooseReadNML2(filename, verbose = 1) + filename = os.path.join(SCRIPT_DIR, 'nml_files/MScellupdated_primDend.nml') + moose.mooseReadNML2(filename, verbose=1) # Define the variables needed to view the underlying curves for the channel kinetics plot_powers = True @@ -145,7 +194,7 @@ def test_nml2_jkopsick(): # currently show up due to the error # in copying the CaPool mechanism, but works if you run it manually after # the code completes. - libchan=moose.element('/library'+ '/' + 'SKCa') + libchan = moose.element('/library' + '/' + 'SKCa') plot_gate_params(libchan, plot_powers, VMIN, VMAX, CAMIN, CAMAX) stamp = datetime.datetime.now().isoformat() @@ -153,8 +202,11 @@ def test_nml2_jkopsick(): plt.tight_layout() plt.savefig(__file__ + ".png") + def main(): + test_nml2() test_nml2_jkopsick() + if __name__ == '__main__': main() From dd7caea342ea96a5e24c3215a262609032c15939 Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Fri, 13 Mar 2020 13:07:11 +0530 Subject: [PATCH 13/16] pylint fixes. --- python/moose/neuroml2/reader.py | 464 +++++++++++++++++--------------- 1 file changed, 240 insertions(+), 224 deletions(-) diff --git a/python/moose/neuroml2/reader.py b/python/moose/neuroml2/reader.py index 6f3b7b30ca..5f7e32c814 100644 --- a/python/moose/neuroml2/reader.py +++ b/python/moose/neuroml2/reader.py @@ -21,7 +21,8 @@ import moose import logging -logger_ = logging.getLogger('moose.nml2') + +logger_ = logging.getLogger("moose.nml2") logger_.setLevel(logging.INFO) try: @@ -29,8 +30,7 @@ from pyneuroml import pynml except Exception as e: print(e) - print( - "********************************************************************") + print("********************************************************************") print("* ") print("* Please install libNeuroML & pyNeuroML: ") print("* pip install libneuroml") @@ -39,11 +39,10 @@ print("* Requirement for this is lxml: ") print("* apt-get install python-lxml") print("* ") - print( - "********************************************************************") + print("********************************************************************") # these are the gates available. These are prefixed by 'gate' in C++ codebase. -_validMooseHHGateIds = ['X', 'Y', 'Z'] +_validMooseHHGateIds = ["X", "Y", "Z"] def _unique(ls): @@ -72,8 +71,11 @@ def _pairNmlGateWithMooseGates(mGates, nmlGates): for nmlGate in nmlGates: if nmlGate is None: continue - if hasattr(nmlGate, 'id') and nmlGate.id \ - and nmlGate.id.upper() in availableMooseGates: + if ( + hasattr(nmlGate, "id") + and nmlGate.id + and nmlGate.id.upper() in availableMooseGates + ): mapping[nmlGate.id.upper()] = nmlGate availableMooseGates.remove(nmlGate.id.upper()) else: @@ -93,9 +95,9 @@ def _isConcDep(ct): :return: True if Component is depenant on conc, False otherwise. """ - if not hasattr(ct, 'extends'): + if not hasattr(ct, "extends"): return False - if 'ConcDep' in ct.extends: + if "ConcDep" in ct.extends: return True return False @@ -105,8 +107,10 @@ def _findCaConc(): Find a suitable CaConc for computing HHGate tables. This is a hack, though it is likely to work in most cases. """ - caConcs = moose.wildcardFind('/library/##[TYPE=CaConc]') - assert len(caConcs) == 1, "No moose.CaConc found. Currently moose \ + caConcs = moose.wildcardFind("/library/##[TYPE=CaConc]") + assert ( + len(caConcs) == 1 + ), "No moose.CaConc found. Currently moose \ supports HHChannel which depends only on moose.CaConc ." return caConcs[0] @@ -161,13 +165,11 @@ def setEk(comp, erev): def getSegments(nmlcell, component, sg_to_segments): """Get the list of segments the `component` is applied to""" sg = component.segment_groups - #seg = component.segment + # seg = component.segment if sg is None: segments = nmlcell.morphology.segments - elif sg == 'all': # Special case - segments = [ - seg for seglist in sg_to_segments.values() for seg in seglist - ] + elif sg == "all": # Special case + segments = [seg for seglist in sg_to_segments.values() for seg in seglist] else: segments = sg_to_segments[sg] @@ -194,6 +196,7 @@ class NML2Reader(object): creates a passive neuronal morphology `/library/Purk2M9s`. """ + def __init__(self, verbose=False): # micron is the default length unit self.lunit = 1e-6 @@ -213,10 +216,10 @@ def __init__(self, verbose=False): self.includes = {} # Included files mapped to other readers # /library may have alreay been created. - if moose.exists('/library'): - self.lib = moose.element('/library') + if moose.exists("/library"): + self.lib = moose.element("/library") else: - self.lib = moose.Neutral('/library') + self.lib = moose.Neutral("/library") self.id_to_ionChannel = {} @@ -232,12 +235,12 @@ def __init__(self, verbose=False): self._variables = {} def read(self, filename, symmetric=True): - self.doc = loaders.read_neuroml2_file(filename, - include_includes=True, - verbose=self.verbose) + self.doc = loaders.read_neuroml2_file( + filename, include_includes=True, verbose=self.verbose + ) if self.verbose: - print('Parsed NeuroML2 file: %s' % filename) + print("Parsed NeuroML2 file: %s" % filename) self.filename = filename if len(self.doc.networks) >= 1: @@ -268,47 +271,56 @@ def getCellInPopulation(self, pop_id, index): def getComp(self, pop_id, cellIndex, segId): return moose.element( - '%s/%s/%s/%s' % - (self.lib.path, pop_id, cellIndex, - self.seg_id_to_comp_name[self.pop_to_cell_type[pop_id]][segId])) + "%s/%s/%s/%s" + % ( + self.lib.path, + pop_id, + cellIndex, + self.seg_id_to_comp_name[self.pop_to_cell_type[pop_id]][segId], + ) + ) def createPopulations(self): for pop in self.network.populations: - mpop = moose.Neutral('%s/%s' % (self.lib.path, pop.id)) + mpop = moose.Neutral("%s/%s" % (self.lib.path, pop.id)) self.cells_in_populations[pop.id] = {} for i in range(pop.size): - print("Creating %s/%s instances of %s under %s" % - (i, pop.size, pop.component, mpop)) + print( + "Creating %s/%s instances of %s under %s" + % (i, pop.size, pop.component, mpop) + ) self.pop_to_cell_type[pop.id] = pop.component - chid = moose.copy(self.proto_cells[pop.component], mpop, - '%s' % (i)) + chid = moose.copy(self.proto_cells[pop.component], mpop, "%s" % (i)) self.cells_in_populations[pop.id][i] = chid def getInput(self, input_id): - return moose.element('%s/inputs/%s' % (self.lib.path, input_id)) + return moose.element("%s/inputs/%s" % (self.lib.path, input_id)) def createInputs(self): for el in self.network.explicit_inputs: - pop_id = el.target.split('[')[0] - i = el.target.split('[')[1].split(']')[0] + pop_id = el.target.split("[")[0] + i = el.target.split("[")[1].split("]")[0] seg_id = 0 - if '/' in el.target: - seg_id = el.target.split('/')[1] + if "/" in el.target: + seg_id = el.target.split("/")[1] input = self.getInput(el.input) - moose.connect(input, 'output', self.getComp(pop_id, i, seg_id), - 'injectMsg') + moose.connect(input, "output", self.getComp(pop_id, i, seg_id), "injectMsg") for il in self.network.input_lists: for ii in il.input: input = self.getInput(il.component) moose.connect( - input, 'output', - self.getComp(il.populations, ii.get_target_cell_id(), - ii.get_segment_id()), 'injectMsg') + input, + "output", + self.getComp( + il.populations, ii.get_target_cell_id(), ii.get_segment_id() + ), + "injectMsg", + ) def createCellPrototype(self, cell, symmetric=True): """To be completed - create the morphology, channels in prototype""" - nrn = moose.Neuron('%s/%s' % (self.lib.path, cell.id)) + nrn = moose.Neuron("%s/%s" % (self.lib.path, cell.id)) self.proto_cells[cell.id] = nrn self.nml_cells_to_moose[cell.id] = nrn self.moose_to_nml[nrn] = cell @@ -336,17 +348,17 @@ def createMorphology(self, nmlcell, moosecell, symmetric=True): self.seg_id_to_comp_name[nmlcell.id] = {} for seg in segments: if seg.name is not None: - id_to_comp[seg.id] = compclass('%s/%s' % (cellpath, seg.name)) + id_to_comp[seg.id] = compclass("%s/%s" % (cellpath, seg.name)) self.seg_id_to_comp_name[nmlcell.id][seg.id] = seg.name else: - name = 'comp_%s' % seg.id - id_to_comp[seg.id] = compclass('%s/%s' % (cellpath, name)) + name = "comp_%s" % seg.id + id_to_comp[seg.id] = compclass("%s/%s" % (cellpath, name)) self.seg_id_to_comp_name[nmlcell.id][seg.id] = name # Now assign the positions and connect up axial resistance if not symmetric: - src, dst = 'axial', 'raxial' + src, dst = "axial", "raxial" else: - src, dst = 'proximal', 'distal' + src, dst = "proximal", "distal" for segid, comp in id_to_comp.items(): segment = id_to_segment[segid] try: @@ -361,30 +373,31 @@ def createMorphology(self, nmlcell, moosecell, symmetric=True): p0 = parent.distal else: raise Exception( - 'No proximal point and no parent segment for segment: name=%s, id=%s' - % (segment.name, segment.id)) - comp.x0, comp.y0, comp.z0 = (x * self.lunit - for x in map(float, (p0.x, p0.y, - p0.z))) + "No proximal point and no parent segment for segment: name=%s, id=%s" + % (segment.name, segment.id) + ) + comp.x0, comp.y0, comp.z0 = ( + x * self.lunit for x in map(float, (p0.x, p0.y, p0.z)) + ) p1 = segment.distal - comp.x, comp.y, comp.z = (x * self.lunit - for x in map(float, (p1.x, p1.y, p1.z))) - comp.length = np.sqrt((comp.x - comp.x0)**2 + - (comp.y - comp.y0)**2 + - (comp.z - comp.z0)**2) + comp.x, comp.y, comp.z = ( + x * self.lunit for x in map(float, (p1.x, p1.y, p1.z)) + ) + comp.length = np.sqrt( + (comp.x - comp.x0) ** 2 + + (comp.y - comp.y0) ** 2 + + (comp.z - comp.z0) ** 2 + ) # This can pose problem with moose where both ends of # compartment have same diameter. We are averaging the two # - may be splitting the compartment into two is better? - comp.diameter = (float(p0.diameter) + - float(p1.diameter)) * self.lunit / 2 + comp.diameter = (float(p0.diameter) + float(p1.diameter)) * self.lunit / 2 if parent: pcomp = id_to_comp[parent.id] moose.connect(comp, src, pcomp, dst) sg_to_segments = {} for sg in morphology.segment_groups: - sg_to_segments[sg.id] = [ - id_to_segment[m.segments] for m in sg.members - ] + sg_to_segments[sg.id] = [id_to_segment[m.segments] for m in sg.members] for sg in morphology.segment_groups: if not sg.id in sg_to_segments: sg_to_segments[sg.id] = [] @@ -392,8 +405,8 @@ def createMorphology(self, nmlcell, moosecell, symmetric=True): for cseg in sg_to_segments[inc.segment_groups]: sg_to_segments[sg.id].append(cseg) - if not 'all' in sg_to_segments: - sg_to_segments['all'] = [s for s in segments] + if not "all" in sg_to_segments: + sg_to_segments["all"] = [s for s in segments] self._cell_to_sg[nmlcell.id] = sg_to_segments return id_to_comp, id_to_segment, sg_to_segments @@ -403,18 +416,20 @@ def importBiophysics(self, nmlcell, moosecell): according to NeuroML2 cell `nmlcell`.""" bp = nmlcell.biophysical_properties if bp is None: - print('Warning: %s in %s has no biophysical properties' % - (nmlcell.id, self.filename)) + print( + "Warning: %s in %s has no biophysical properties" + % (nmlcell.id, self.filename) + ) return - self.importMembraneProperties(nmlcell, moosecell, - bp.membrane_properties) - self.importIntracellularProperties(nmlcell, moosecell, - bp.intracellular_properties) + self.importMembraneProperties(nmlcell, moosecell, bp.membrane_properties) + self.importIntracellularProperties( + nmlcell, moosecell, bp.intracellular_properties + ) def importMembraneProperties(self, nmlcell, moosecell, mp): """Create the membrane properties from nmlcell in moosecell""" if self.verbose: - print('Importing membrane properties') + print("Importing membrane properties") self.importCapacitances(nmlcell, moosecell, mp.specific_capacitances) self.importChannelsToCell(nmlcell, moosecell, mp) self.importInitMembPotential(nmlcell, moosecell, mp) @@ -442,8 +457,9 @@ def importIntracellularProperties(self, nmlcell, moosecell, properties): def importSpecies(self, nmlcell, properties): sg_to_segments = self._cell_to_sg[nmlcell.id] for species in properties.species: - if (species.concentration_model is not None) and \ - (species.concentration_model not in self.proto_pools): + if (species.concentration_model is not None) and ( + species.concentration_model not in self.proto_pools + ): continue segments = getSegments(nmlcell, species, sg_to_segments) for seg in segments: @@ -459,17 +475,21 @@ def copySpecies(self, species, compartment): else: for innerReader in self.includes.values(): if species.concentration_model in innerReader.proto_pools: - proto_pool = innerReader.proto_pools[ - species.concentration_model] + proto_pool = innerReader.proto_pools[species.concentration_model] break if not proto_pool: - raise Exception('No prototype pool for %s referred to by %s' % - (species.concentration_model, species.id)) + raise Exception( + "No prototype pool for %s referred to by %s" + % (species.concentration_model, species.id) + ) pool_id = moose.copy(proto_pool, compartment, species.id) pool = moose.element(pool_id) - pool.B = pool.B / (np.pi * compartment.length * - (0.5 * compartment.diameter + pool.thick) * - (0.5 * compartment.diameter - pool.thick)) + pool.B = pool.B / ( + np.pi + * compartment.length + * (0.5 * compartment.diameter + pool.thick) + * (0.5 * compartment.diameter - pool.thick) + ) return pool def importAxialResistance(self, nmlcell, intracellularProperties): @@ -481,32 +501,27 @@ def importAxialResistance(self, nmlcell, intracellularProperties): setRa(comp, SI(r.value)) def isPassiveChan(self, chan): - if chan.type == 'ionChannelPassive': + if chan.type == "ionChannelPassive": return True - if hasattr(chan, 'gates'): + if hasattr(chan, "gates"): return len(chan.gate_hh_rates) + len(chan.gates) == 0 return False rate_fn_map = { - 'HHExpRate': exponential2, - 'HHSigmoidRate': sigmoid2, - 'HHSigmoidVariable': sigmoid2, - 'HHExpLinearRate': linoid2 + "HHExpRate": exponential2, + "HHSigmoidRate": sigmoid2, + "HHSigmoidVariable": sigmoid2, + "HHExpLinearRate": linoid2, } - def calculateRateFn(self, - ratefn, - mgate, - vmin, - vmax, - tablen=3000, - vShift='0mV'): + def calculateRateFn(self, ratefn, mgate, vmin, vmax, tablen=3000, vShift="0mV"): """Returns A / B table from ngate.""" tab = np.linspace(vmin, vmax, tablen) if self._is_standard_nml_rate(ratefn): midpoint, rate, scale = map( - SI, (ratefn.midpoint, ratefn.rate, ratefn.scale)) + SI, (ratefn.midpoint, ratefn.rate, ratefn.scale) + ) return self.rate_fn_map[ratefn.type](tab, rate, scale, midpoint) for ct in self.doc.ComponentType: @@ -517,109 +532,79 @@ def calculateRateFn(self, return self._computeRateFn(ct, tab) else: ca = _findCaConc() - if _whichGate(mgate) != 'Z': + if _whichGate(mgate) != "Z": raise RuntimeWarning( "Concentration dependant gate " " should use gateZ of moose.HHChannel. " " If you know what you are doing, ignore this " - " warning. ") + " warning. " + ) return self._computeRateFnCa(ca, ct, tab, vShift=vShift) def _computeRateFnCa(self, ca, ct, tab, vShift): rate = [] for v in tab: req_vars = { - ca.name: '%sV' % v, - 'vShift': vShift, - 'temperature': self._getTemperature() + ca.name: "%sV" % v, + "vShift": vShift, + "temperature": self._getTemperature(), } req_vars.update(self._variables) vals = pynml.evaluate_component(ct, req_variables=req_vars) - '''print(vals)''' - if 'x' in vals: - rate.append(vals['x']) - if 't' in vals: - rate.append(vals['t']) - if 'r' in vals: - rate.append(vals['r']) + """print(vals)""" + if "x" in vals: + rate.append(vals["x"]) + if "t" in vals: + rate.append(vals["t"]) + if "r" in vals: + rate.append(vals["r"]) return np.array(rate) - def _computeRateFn(self, ct, tab, vShift): + def _computeRateFn(self, ct, tab, vShift=0): rate = [] for v in tab: req_vars = { - 'v': '%sV' % v, - 'vShift': vShift, - 'temperature': self._getTemperature() + "v": "%sV" % v, + "vShift": vShift, + "temperature": self._getTemperature(), } req_vars.update(self._variables) vals = pynml.evaluate_component(ct, req_variables=req_vars) - '''print(vals)''' - if 'x' in vals: - rate.append(vals['x']) - if 't' in vals: - rate.append(vals['t']) - if 'r' in vals: - rate.append(vals['r']) + """print(vals)""" + if "x" in vals: + rate.append(vals["x"]) + if "t" in vals: + rate.append(vals["t"]) + if "r" in vals: + rate.append(vals["r"]) return np.array(rate) - def calculateRateFnCaDep(self, - ratefn, - ca, - camin, - camax, - tablen=3000, - vShift='0mV'): - """Returns A / B table from ngate. - - This function compute rate which depends on ca. - This must go into Z gate. - """ - tab = np.linspace(camin, camax, tablen) - if self._is_standard_nml_rate(ratefn): - midpoint, rate, scale = map( - SI, (ratefn.midpoint, ratefn.rate, ratefn.scale)) - return self.rate_fn_map[ratefn.type](tab, rate, scale, midpoint) - - for ct in self.doc.ComponentType: - if ratefn.type != ct.name: - continue - logger_.info("Using %s to evaluate rate (caConc dependant)" % - ct.name) - rate = [] - for v in tab: - req_vars = { - ca.name: '%sV' % v, - 'vShift': vShift, - 'temperature': self._getTemperature() - } - vals = pynml.evaluate_component(ct, req_variables=req_vars) - '''print(vals)''' - if 'x' in vals: - rate.append(vals['x']) - if 't' in vals: - rate.append(vals['t']) - if 'r' in vals: - rate.append(vals['r']) - return np.array(rate) - def importChannelsToCell(self, nmlcell, moosecell, membrane_properties): sg_to_segments = self._cell_to_sg[nmlcell.id] - for chdens in membrane_properties.channel_densities + membrane_properties.channel_density_v_shifts: + for chdens in ( + membrane_properties.channel_densities + + membrane_properties.channel_density_v_shifts + ): segments = getSegments(nmlcell, chdens, sg_to_segments) condDensity = SI(chdens.cond_density) erev = SI(chdens.erev) try: ionChannel = self.id_to_ionChannel[chdens.ion_channel] except KeyError: - print('No channel with id', chdens.ion_channel) + print("No channel with id", chdens.ion_channel) continue if self.verbose: print( - 'Setting density of channel %s in %s to %s; erev=%s (passive: %s)' - % (chdens.id, segments, condDensity, erev, - self.isPassiveChan(ionChannel))) + "Setting density of channel %s in %s to %s; erev=%s (passive: %s)" + % ( + chdens.id, + segments, + condDensity, + erev, + self.isPassiveChan(ionChannel), + ) + ) if self.isPassiveChan(ionChannel): for seg in segments: @@ -628,10 +613,11 @@ def importChannelsToCell(self, nmlcell, moosecell, membrane_properties): setEk(comp, erev) else: for seg in segments: - self.copyChannel(chdens, self.nml_segs_to_moose[seg.id], - condDensity, erev) - '''moose.le(self.nml_segs_to_moose[seg.id]) - moose.showfield(self.nml_segs_to_moose[seg.id], field="*", showtype=True)''' + self.copyChannel( + chdens, self.nml_segs_to_moose[seg.id], condDensity, erev + ) + """moose.le(self.nml_segs_to_moose[seg.id]) + moose.showfield(self.nml_segs_to_moose[seg.id], field="*", showtype=True)""" def copyChannel(self, chdens, comp, condDensity, erev): """Copy moose prototype for `chdens` condutcance density to `comp` @@ -647,27 +633,31 @@ def copyChannel(self, chdens, comp, condDensity, erev): proto_chan = innerReader.proto_chans[chdens.ion_channel] break if not proto_chan: - raise Exception('No prototype channel for %s referred to by %s' % - (chdens.ion_channel, chdens.id)) + raise Exception( + "No prototype channel for %s referred to by %s" + % (chdens.ion_channel, chdens.id) + ) if self.verbose: - print('Copying %s to %s, %s; erev=%s' % - (chdens.id, comp, condDensity, erev)) + print( + "Copying %s to %s, %s; erev=%s" % (chdens.id, comp, condDensity, erev) + ) orig = chdens.id chid = moose.copy(proto_chan, comp, chdens.id) chan = moose.element(chid) els = list(self.paths_to_chan_elements.keys()) for p in els: - pp = p.replace('%s/' % chdens.ion_channel, '%s/' % orig) - self.paths_to_chan_elements[pp] = self.paths_to_chan_elements[ - p].replace('%s/' % chdens.ion_channel, '%s/' % orig) - #print(self.paths_to_chan_elements) + pp = p.replace("%s/" % chdens.ion_channel, "%s/" % orig) + self.paths_to_chan_elements[pp] = self.paths_to_chan_elements[p].replace( + "%s/" % chdens.ion_channel, "%s/" % orig + ) + # print(self.paths_to_chan_elements) chan.Gbar = sarea(comp) * condDensity chan.Ek = erev - moose.connect(chan, 'channel', comp, 'channel') + moose.connect(chan, "channel", comp, "channel") return chan - ''' + """ def importIncludes(self, doc): for include in doc.include: if self.verbose: @@ -691,30 +681,32 @@ def importIncludes(self, doc): break if error: print(self.filename, 'Last exception:', error) - raise IOError('Could not read any of the locations: %s' % (paths))''' + raise IOError('Could not read any of the locations: %s' % (paths))""" def _is_standard_nml_rate(self, rate): - return rate.type=='HHExpLinearRate' \ - or rate.type=='HHExpRate' or \ - rate.type=='HHSigmoidRate' or \ - rate.type=='HHSigmoidVariable' + return ( + rate.type == "HHExpLinearRate" + or rate.type == "HHExpRate" + or rate.type == "HHSigmoidRate" + or rate.type == "HHSigmoidVariable" + ) def createHHChannel(self, chan, vmin=-150e-3, vmax=100e-3, vdivs=5000): - path = '%s/%s' % (self.lib.path, chan.id) + path = "%s/%s" % (self.lib.path, chan.id) if moose.exists(path): mchan = moose.element(path) else: mchan = moose.HHChannel(path) - mgates = [ - moose.element(g) for g in [mchan.gateX, mchan.gateY, mchan.gateZ] - ] + mgates = [moose.element(g) for g in [mchan.gateX, mchan.gateY, mchan.gateZ]] # We handle only up to 3 gates in HHCHannel assert len(chan.gate_hh_rates) <= 3, "No more than 3 gates" if self.verbose: - print('== Creating channel: %s (%s) -> %s (%s)' % - (chan.id, chan.gate_hh_rates, mchan, mgates)) + print( + "== Creating channel: %s (%s) -> %s (%s)" + % (chan.id, chan.gate_hh_rates, mchan, mgates) + ) all_gates = chan.gates + chan.gate_hh_rates @@ -722,13 +714,11 @@ def createHHChannel(self, chan, vmin=-150e-3, vmax=100e-3, vdivs=5000): # with moose.HHChannel's gateX, gateY, gateZ respectively. Else pair # them with gateX, gateY, gateZ acording to list order. for mgate, ngate in _pairNmlGateWithMooseGates(mgates, all_gates): - self._addGateToHHChannel(chan, mchan, mgate, ngate, vmin, vmax, - vdivs) - logger_.debug('== Created', mchan.path, 'for', chan.id) + self._addGateToHHChannel(chan, mchan, mgate, ngate, vmin, vmax, vdivs) + logger_.debug("== Created %s for %s" % (mchan.path, chan.id)) return mchan - def _addGateToHHChannel(self, chan, mchan, mgate, ngate, vmin, vmax, - vdivs): + def _addGateToHHChannel(self, chan, mchan, mgate, ngate, vmin, vmax, vdivs): """Add gateX, gateY, gateZ etc to moose.HHChannel (mchan). Each gate can be voltage dependant and/or concentration dependant. @@ -736,7 +726,7 @@ def _addGateToHHChannel(self, chan, mchan, mgate, ngate, vmin, vmax, """ # set mgate.Xpower, .Ypower etc. - setattr(mchan, _whichGate(mgate) + 'power', ngate.instances) + setattr(mchan, _whichGate(mgate) + "power", ngate.instances) mgate.min = vmin mgate.max = vmax @@ -752,27 +742,39 @@ def _addGateToHHChannel(self, chan, mchan, mgate, ngate, vmin, vmax, fwd = ngate.forward_rate rev = ngate.reverse_rate - self.paths_to_chan_elements['%s/%s'%(chan.id, ngate.id)] = \ - '%s/%s' % (chan.id, mgate.name) + self.paths_to_chan_elements["%s/%s" % (chan.id, ngate.id)] = "%s/%s" % ( + chan.id, + mgate.name, + ) q10_scale = 1 if ngate.q10_settings: - if ngate.q10_settings.type == 'q10Fixed': + if ngate.q10_settings.type == "q10Fixed": q10_scale = float(ngate.q10_settings.fixed_q10) - elif ngate.q10_settings.type == 'q10ExpTemp': + elif ngate.q10_settings.type == "q10ExpTemp": q10_scale = math.pow( float(ngate.q10_settings.q10_factor), - (self._getTemperature() - - SI(ngate.q10_settings.experimental_temp)) / 10) + (self._getTemperature() - SI(ngate.q10_settings.experimental_temp)) + / 10, + ) logger_.debug( - 'Q10: %s; %s; %s; %s' % - (ngate.q10_settings.q10_factor, self._getTemperature(), - SI(ngate.q10_settings.experimental_temp), q10_scale)) + "Q10: %s; %s; %s; %s" + % ( + ngate.q10_settings.q10_factor, + self._getTemperature(), + SI(ngate.q10_settings.experimental_temp), + q10_scale, + ) + ) else: - raise Exception('Unknown Q10 scaling type %s: %s' % - (ngate.q10_settings.type, ngate.q10_settings)) - logger_.info(' === Gate: %s; %s; %s; %s; %s; scale=%s' % - (ngate.id, mgate.path, mchan.Xpower, fwd, rev, q10_scale)) + raise Exception( + "Unknown Q10 scaling type %s: %s" + % (ngate.q10_settings.type, ngate.q10_settings) + ) + logger_.info( + " === Gate: %s; %s; %s; %s; %s; scale=%s" + % (ngate.id, mgate.path, mchan.Xpower, fwd, rev, q10_scale) + ) if (fwd is not None) and (rev is not None): # Note: MOOSE HHGate are either voltage of concentration @@ -785,8 +787,12 @@ def _addGateToHHChannel(self, chan, mchan, mgate, ngate, vmin, vmax, mgate.tableB = q10_scale * (alpha + beta) # Assuming the meaning of the elements in GateHHTauInf ... - if hasattr(ngate,'time_course') and hasattr(ngate,'steady_state') \ - and (ngate.time_course is not None) and (ngate.steady_state is not None): + if ( + hasattr(ngate, "time_course") + and hasattr(ngate, "steady_state") + and (ngate.time_course is not None) + and (ngate.steady_state is not None) + ): tau = ngate.time_course inf = ngate.steady_state tau = self.calculateRateFn(tau, mgate, vmin, vmax, vdivs) @@ -794,27 +800,30 @@ def _addGateToHHChannel(self, chan, mchan, mgate, ngate, vmin, vmax, mgate.tableA = q10_scale * (inf / tau) mgate.tableB = q10_scale * (1 / tau) - if hasattr(ngate, 'steady_state') and (ngate.time_course is None) \ - and (ngate.steady_state is not None): + if ( + hasattr(ngate, "steady_state") + and (ngate.time_course is None) + and (ngate.steady_state is not None) + ): inf = ngate.steady_state tau = 1 / (alpha + beta) - if (inf is not None): + if inf is not None: inf = self.calculateRateFn(inf, vmin, vmax, vdivs) mgate.tableA = q10_scale * (inf / tau) mgate.tableB = q10_scale * (1 / tau) def createPassiveChannel(self, chan): - mchan = moose.Leakage('%s/%s' % (self.lib.path, chan.id)) + mchan = moose.Leakage("%s/%s" % (self.lib.path, chan.id)) if self.verbose: - print(self.filename, 'Created', mchan.path, 'for', chan.id) + print(self.filename, "Created", mchan.path, "for", chan.id) return mchan def importInputs(self, doc): - minputs = moose.Neutral('%s/inputs' % (self.lib.path)) + minputs = moose.Neutral("%s/inputs" % (self.lib.path)) for pg_nml in doc.pulse_generators: assert pg_nml.id - pg = moose.PulseGen('%s/%s' % (minputs.path, pg_nml.id)) + pg = moose.PulseGen("%s/%s" % (minputs.path, pg_nml.id)) pg.firstDelay = SI(pg_nml.delay) pg.firstWidth = SI(pg_nml.duration) pg.firstLevel = SI(pg_nml.amplitude) @@ -822,10 +831,10 @@ def importInputs(self, doc): def importIonChannels(self, doc, vmin=-150e-3, vmax=100e-3, vdivs=5000): if self.verbose: - print(self.filename, 'Importing the ion channels') + print(self.filename, "Importing the ion channels") for chan in doc.ion_channel + doc.ion_channel_hhs: - if chan.type == 'ionChannelHH': + if chan.type == "ionChannelHH": mchan = self.createHHChannel(chan) elif self.isPassiveChan(chan): mchan = self.createPassiveChannel(chan) @@ -837,8 +846,14 @@ def importIonChannels(self, doc, vmin=-150e-3, vmax=100e-3, vdivs=5000): self.nml_chans_to_moose[chan.id] = mchan self.proto_chans[chan.id] = mchan if self.verbose: - print(self.filename, 'Created ion channel', mchan.path, 'for', - chan.type, chan.id) + print( + self.filename, + "Created ion channel", + mchan.path, + "for", + chan.type, + chan.id, + ) def importConcentrationModels(self, doc): for concModel in doc.decaying_pool_concentration_models: @@ -849,9 +864,9 @@ def createDecayingPoolConcentrationModel(self, concModel): """Create prototype for concentration model""" assert concModel.id, "Empty id is not allowed" name = concModel.id - if hasattr(concModel, 'name') and concModel.name is not None: + if hasattr(concModel, "name") and concModel.name is not None: name = concModel.name - ca = moose.CaConc('%s/%s' % (self.lib.path, name)) + ca = moose.CaConc("%s/%s" % (self.lib.path, name)) ca.CaBasal = SI(concModel.resting_conc) ca.tau = SI(concModel.decay_constant) @@ -862,5 +877,6 @@ def createDecayingPoolConcentrationModel(self, concModel): self.proto_pools[concModel.id] = ca self.nml_concs_to_moose[concModel.id] = ca self.moose_to_nml[ca] = concModel - logger_.debug('Created moose element: %s for nml conc %s' % - (ca.path, concModel.id)) + logger_.debug( + "Created moose element: %s for nml conc %s" % (ca.path, concModel.id) + ) From fdedfb26ffa497a1ea2a04dd75de7a51386a4174 Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Fri, 13 Mar 2020 13:42:03 +0530 Subject: [PATCH 14/16] Added missing nml2 files. --- tests/support/nml_files/CaPool.nml | 10 + tests/support/nml_files/LEMS_hhCell.xml | 43 + tests/support/nml_files/LEMS_passiveCell.xml | 33 + .../nml_files/MScellupdated_primDend.nml | 113 + tests/support/nml_files/NML2_FullCell.nml | 109 + .../nml_files/NML2_SingleCompHHCell.nml | 90 + tests/support/nml_files/Purk2M9s.nml | 20834 ++++++++++++++++ tests/support/nml_files/SKchannel2.nml | 107 + tests/support/nml_files/SimpleIonChannel.xml | 24 + tests/support/nml_files/pas.channel.nml | 12 + tests/support/nml_files/passiveCell.nml | 52 + 11 files changed, 21427 insertions(+) create mode 100644 tests/support/nml_files/CaPool.nml create mode 100644 tests/support/nml_files/LEMS_hhCell.xml create mode 100644 tests/support/nml_files/LEMS_passiveCell.xml create mode 100644 tests/support/nml_files/MScellupdated_primDend.nml create mode 100644 tests/support/nml_files/NML2_FullCell.nml create mode 100644 tests/support/nml_files/NML2_SingleCompHHCell.nml create mode 100644 tests/support/nml_files/Purk2M9s.nml create mode 100644 tests/support/nml_files/SKchannel2.nml create mode 100644 tests/support/nml_files/SimpleIonChannel.xml create mode 100644 tests/support/nml_files/pas.channel.nml create mode 100644 tests/support/nml_files/passiveCell.nml diff --git a/tests/support/nml_files/CaPool.nml b/tests/support/nml_files/CaPool.nml new file mode 100644 index 0000000000..2358c75b5d --- /dev/null +++ b/tests/support/nml_files/CaPool.nml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/tests/support/nml_files/LEMS_hhCell.xml b/tests/support/nml_files/LEMS_hhCell.xml new file mode 100644 index 0000000000..09363c309e --- /dev/null +++ b/tests/support/nml_files/LEMS_hhCell.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/support/nml_files/LEMS_passiveCell.xml b/tests/support/nml_files/LEMS_passiveCell.xml new file mode 100644 index 0000000000..f6e48f6842 --- /dev/null +++ b/tests/support/nml_files/LEMS_passiveCell.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/support/nml_files/MScellupdated_primDend.nml b/tests/support/nml_files/MScellupdated_primDend.nml new file mode 100644 index 0000000000..26d9c80727 --- /dev/null +++ b/tests/support/nml_files/MScellupdated_primDend.nml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/NML2_FullCell.nml b/tests/support/nml_files/NML2_FullCell.nml new file mode 100644 index 0000000000..6ad0941ac2 --- /dev/null +++ b/tests/support/nml_files/NML2_FullCell.nml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + A Simple Spiking cell for testing purposes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/NML2_SingleCompHHCell.nml b/tests/support/nml_files/NML2_SingleCompHHCell.nml new file mode 100644 index 0000000000..1b1d43b926 --- /dev/null +++ b/tests/support/nml_files/NML2_SingleCompHHCell.nml @@ -0,0 +1,90 @@ + + + + + + + + + + Leak conductance + + + + + Na channel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/Purk2M9s.nml b/tests/support/nml_files/Purk2M9s.nml new file mode 100644 index 0000000000..511ab43e06 --- /dev/null +++ b/tests/support/nml_files/Purk2M9s.nml @@ -0,0 +1,20834 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/SKchannel2.nml b/tests/support/nml_files/SKchannel2.nml new file mode 100644 index 0000000000..9c2605d67a --- /dev/null +++ b/tests/support/nml_files/SKchannel2.nml @@ -0,0 +1,107 @@ + + + + + + + Small-conductance, Ca2+ activated K+ current + +Comment from original mod file: +: SK-type calcium-activated potassium current +: Reference : Kohler et al. 1996 + + + + + + + + Models of Neocortical Layer 5b Pyramidal Cells Capturing a Wide Range of Dendritic and Perisomatic Active Properties, + Etay Hay, Sean Hill, Felix Schürmann, Henry Markram and Idan Segev, PLoS Comp Biol 2011 + + + + + + + + K channels + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/SimpleIonChannel.xml b/tests/support/nml_files/SimpleIonChannel.xml new file mode 100644 index 0000000000..02188151df --- /dev/null +++ b/tests/support/nml_files/SimpleIonChannel.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/pas.channel.nml b/tests/support/nml_files/pas.channel.nml new file mode 100644 index 0000000000..16ed312a3e --- /dev/null +++ b/tests/support/nml_files/pas.channel.nml @@ -0,0 +1,12 @@ + + + + NeuroML file containing a single Channel description + + + + Simple example of a leak/passive conductance. + + + + diff --git a/tests/support/nml_files/passiveCell.nml b/tests/support/nml_files/passiveCell.nml new file mode 100644 index 0000000000..9b91f2530d --- /dev/null +++ b/tests/support/nml_files/passiveCell.nml @@ -0,0 +1,52 @@ + + + + + + + Leak conductance + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 5324a3c9bf535c1c72ca6c33ac9ca4d8c73d5283 Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Fri, 13 Mar 2020 16:06:25 +0530 Subject: [PATCH 15/16] Needs a xml convertor to conver NML to MOOSEML. --- python/moose/neuroml2/reader.py | 51 +++++- .../support/nml_files/NML2_AbstractCells.nml | 44 +++++ .../support/nml_files/NML2_AnalogSynapses.nml | 56 +++++++ .../nml_files/NML2_AnalogSynapsesHH.nml | 56 +++++++ tests/support/nml_files/NML2_FullCell.nml | 4 +- tests/support/nml_files/NML2_FullNeuroML.nml | 117 +++++++++++++ .../nml_files/NML2_GapJunctionInstances.nml | 57 +++++++ tests/support/nml_files/NML2_GapJunctions.nml | 39 +++++ .../nml_files/NML2_InhomogeneousParams.nml | 86 ++++++++++ tests/support/nml_files/NML2_Inputs.nml | 76 +++++++++ .../nml_files/NML2_InstanceBasedNetwork.nml | 61 +++++++ .../nml_files/NML2_MultiCompCellNetwork.nml | 155 ++++++++++++++++++ tests/support/nml_files/NML2_PyNNCells.nml | 79 +++++++++ .../nml_files/NML2_SegmentConnections.nml | 75 +++++++++ .../nml_files/NML2_SimpleIonChannel.nml | 24 +++ .../nml_files/NML2_SimpleMorphology.nml | 91 ++++++++++ tests/support/nml_files/NML2_SynapseTypes.nml | 80 +++++++++ tests/support/nml_files/README | 1 + tests/support/test_nml2.py | 14 ++ 19 files changed, 1160 insertions(+), 6 deletions(-) create mode 100644 tests/support/nml_files/NML2_AbstractCells.nml create mode 100644 tests/support/nml_files/NML2_AnalogSynapses.nml create mode 100644 tests/support/nml_files/NML2_AnalogSynapsesHH.nml create mode 100644 tests/support/nml_files/NML2_FullNeuroML.nml create mode 100644 tests/support/nml_files/NML2_GapJunctionInstances.nml create mode 100644 tests/support/nml_files/NML2_GapJunctions.nml create mode 100644 tests/support/nml_files/NML2_InhomogeneousParams.nml create mode 100644 tests/support/nml_files/NML2_Inputs.nml create mode 100644 tests/support/nml_files/NML2_InstanceBasedNetwork.nml create mode 100644 tests/support/nml_files/NML2_MultiCompCellNetwork.nml create mode 100644 tests/support/nml_files/NML2_PyNNCells.nml create mode 100644 tests/support/nml_files/NML2_SegmentConnections.nml create mode 100644 tests/support/nml_files/NML2_SimpleIonChannel.nml create mode 100644 tests/support/nml_files/NML2_SimpleMorphology.nml create mode 100644 tests/support/nml_files/NML2_SynapseTypes.nml create mode 100644 tests/support/nml_files/README diff --git a/python/moose/neuroml2/reader.py b/python/moose/neuroml2/reader.py index 5f7e32c814..33075754db 100644 --- a/python/moose/neuroml2/reader.py +++ b/python/moose/neuroml2/reader.py @@ -59,6 +59,21 @@ def _whichGate(chan): assert c in _validMooseHHGateIds return c +def _setAttrFromNMLAttr(mObj, mAttr, nObj, nAttr, convertToSI=False): + """Set MOOSE's object attribute from NML attribute + + :param mObj: MOOSE object. + :param mAttr: MOOSE object's attribute + :param nObj: NML2 Object + :param nAttr: NML2 Object attribute. + :param convertToSI: If `True` convert value to si unit. + """ + if not hasattr(nObj, nAttr): + return + if not hasattr(mObj, mAttr): + return + val = SI(getattr(nObj, nAttr)) if convertToSI else getattr(nObj, nAttr) + setattr(mObj, mAttr, val) def _pairNmlGateWithMooseGates(mGates, nmlGates): """Return moose gate id from nml.HHGate @@ -255,6 +270,9 @@ def read(self, filename, symmetric=True): for cell in self.doc.cells: self.createCellPrototype(cell, symmetric=symmetric) + for iaf in self.doc.iaf_cells: + self.createIAFCellPrototype(iaf) + if len(self.doc.networks) >= 1: self.createPopulations() self.createInputs() @@ -285,12 +303,11 @@ def createPopulations(self): mpop = moose.Neutral("%s/%s" % (self.lib.path, pop.id)) self.cells_in_populations[pop.id] = {} for i in range(pop.size): - print( - "Creating %s/%s instances of %s under %s" - % (i, pop.size, pop.component, mpop) + logger_.info("Creating (%d out of %d) instances of %s (Type %s) under %s" + % (i, pop.size, pop.id, pop.component, mpop) ) self.pop_to_cell_type[pop.id] = pop.component - chid = moose.copy(self.proto_cells[pop.component], mpop, "%s" % (i)) + chid = moose.copy(self.proto_cells[pop.component], mpop, "%d"%i) self.cells_in_populations[pop.id][i] = chid def getInput(self, input_id): @@ -318,6 +335,32 @@ def createInputs(self): "injectMsg", ) + def createIAFCellPrototype(self, iaf): + """FIXME: Not tested. + """ + mLIF = moose.LIF("%s/%s" % (self.lib.path, iaf.id)) + _setAttrFromNMLAttr(mLIF, 'vReset', iaf, 'reset', True) + _setAttrFromNMLAttr(mLIF, 'thres', iaf, 'thres', True) + _setAttrFromNMLAttr(mLIF, 'refractoryPeriod', iaf, 'refrac', True) + _setAttrFromNMLAttr(mLIF, 'Cm', iaf, 'C', True) + _setAttrFromNMLAttr(mLIF, 'Ra', iaf, 'Ra', True) + + if iaf.leak_conductance: + mLIF.Rm = 1.0/SI(iaf.leak_conductance) + + if hasattr(iaf, 'leak_reversal'): + logger_.warning("moose.LIF does not supprot leakReversal") + + self.proto_cells[iaf.id] = mLIF + self.nml_cells_to_moose[iaf.id] = mLIF + self.moose_to_nml[mLIF] = iaf + + + quit() + # self.createMorphology(cell, nrn, symmetric=symmetric) + # self.importBiophysics(cell, nrn) + return iaf, mLIF + def createCellPrototype(self, cell, symmetric=True): """To be completed - create the morphology, channels in prototype""" nrn = moose.Neuron("%s/%s" % (self.lib.path, cell.id)) diff --git a/tests/support/nml_files/NML2_AbstractCells.nml b/tests/support/nml_files/NML2_AbstractCells.nml new file mode 100644 index 0000000000..ca53f04481 --- /dev/null +++ b/tests/support/nml_files/NML2_AbstractCells.nml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/NML2_AnalogSynapses.nml b/tests/support/nml_files/NML2_AnalogSynapses.nml new file mode 100644 index 0000000000..6d949deae2 --- /dev/null +++ b/tests/support/nml_files/NML2_AnalogSynapses.nml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/NML2_AnalogSynapsesHH.nml b/tests/support/nml_files/NML2_AnalogSynapsesHH.nml new file mode 100644 index 0000000000..d311651f06 --- /dev/null +++ b/tests/support/nml_files/NML2_AnalogSynapsesHH.nml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/NML2_FullCell.nml b/tests/support/nml_files/NML2_FullCell.nml index 6ad0941ac2..fffc74f53b 100644 --- a/tests/support/nml_files/NML2_FullCell.nml +++ b/tests/support/nml_files/NML2_FullCell.nml @@ -12,9 +12,9 @@ - + - + diff --git a/tests/support/nml_files/NML2_FullNeuroML.nml b/tests/support/nml_files/NML2_FullNeuroML.nml new file mode 100644 index 0000000000..a824e3e87f --- /dev/null +++ b/tests/support/nml_files/NML2_FullNeuroML.nml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/NML2_GapJunctionInstances.nml b/tests/support/nml_files/NML2_GapJunctionInstances.nml new file mode 100644 index 0000000000..79f80c1b1e --- /dev/null +++ b/tests/support/nml_files/NML2_GapJunctionInstances.nml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/NML2_GapJunctions.nml b/tests/support/nml_files/NML2_GapJunctions.nml new file mode 100644 index 0000000000..7d44119021 --- /dev/null +++ b/tests/support/nml_files/NML2_GapJunctions.nml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/NML2_InhomogeneousParams.nml b/tests/support/nml_files/NML2_InhomogeneousParams.nml new file mode 100644 index 0000000000..3172cd683d --- /dev/null +++ b/tests/support/nml_files/NML2_InhomogeneousParams.nml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/support/nml_files/NML2_Inputs.nml b/tests/support/nml_files/NML2_Inputs.nml new file mode 100644 index 0000000000..e466ad9a7c --- /dev/null +++ b/tests/support/nml_files/NML2_Inputs.nml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/NML2_InstanceBasedNetwork.nml b/tests/support/nml_files/NML2_InstanceBasedNetwork.nml new file mode 100644 index 0000000000..d8a2e0624b --- /dev/null +++ b/tests/support/nml_files/NML2_InstanceBasedNetwork.nml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/NML2_MultiCompCellNetwork.nml b/tests/support/nml_files/NML2_MultiCompCellNetwork.nml new file mode 100644 index 0000000000..063658cde3 --- /dev/null +++ b/tests/support/nml_files/NML2_MultiCompCellNetwork.nml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + Multicompartmental cell + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/NML2_PyNNCells.nml b/tests/support/nml_files/NML2_PyNNCells.nml new file mode 100644 index 0000000000..ea50b4c5ec --- /dev/null +++ b/tests/support/nml_files/NML2_PyNNCells.nml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/support/nml_files/NML2_SegmentConnections.nml b/tests/support/nml_files/NML2_SegmentConnections.nml new file mode 100644 index 0000000000..ebf2136762 --- /dev/null +++ b/tests/support/nml_files/NML2_SegmentConnections.nml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/NML2_SimpleIonChannel.nml b/tests/support/nml_files/NML2_SimpleIonChannel.nml new file mode 100644 index 0000000000..83864f42ed --- /dev/null +++ b/tests/support/nml_files/NML2_SimpleIonChannel.nml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/NML2_SimpleMorphology.nml b/tests/support/nml_files/NML2_SimpleMorphology.nml new file mode 100644 index 0000000000..2f06956a6a --- /dev/null +++ b/tests/support/nml_files/NML2_SimpleMorphology.nml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/support/nml_files/NML2_SynapseTypes.nml b/tests/support/nml_files/NML2_SynapseTypes.nml new file mode 100644 index 0000000000..b2084b00e4 --- /dev/null +++ b/tests/support/nml_files/NML2_SynapseTypes.nml @@ -0,0 +1,80 @@ + + + + + + + + An alpha synapse with time for rise equal to decay. + + + + + A simple monoexponential synapse. + + + + + A biexponential synapse. + + + + + A synapse consisting of one rise and two decay time courses. + + + + + A biexponential synapse exhibiting STD. + + + + + + A biexponential synapse with short term depression + and facilitation. + + + + + + A biexponential blocking synapse, with STD. + + + + + + + A biexponential blocking synapse with short term + depression and facilitation. + + + + + + + + + + A single "synapse" which contains both AMPA and NMDA. It is planned that the need for extra synapse1Path/synapse2Path attributes can be removed in later versions. + + + diff --git a/tests/support/nml_files/README b/tests/support/nml_files/README new file mode 100644 index 0000000000..4bb616d5f2 --- /dev/null +++ b/tests/support/nml_files/README @@ -0,0 +1 @@ +Files starting with NML2_ in this folder will be valid against ../Schemas/NeuroML2/NeuroML_v2beta.xsd diff --git a/tests/support/test_nml2.py b/tests/support/test_nml2.py index e048575a20..f6a0daf773 100644 --- a/tests/support/test_nml2.py +++ b/tests/support/test_nml2.py @@ -202,8 +202,22 @@ def test_nml2_jkopsick(): plt.tight_layout() plt.savefig(__file__ + ".png") +def test_parse_nml_files(): + import glob + files = glob.glob(os.path.join(SCRIPT_DIR, 'nml_files', '*.nml')) + print("Total %s files found" % len(files)) + for f in files: + if moose.exists('/model'): moose.delete('/model') + if moose.exists('/library'): moose.delete('/library') + print("\n\n=========================================") + print("[INFO ] Reading file %s" % f) + moose.mooseReadNML2(f) + + quit() + def main(): + test_parse_nml_files() test_nml2() test_nml2_jkopsick() From b199f5c8f35927985b9b1b2e3a77cd238844f930 Mon Sep 17 00:00:00 2001 From: Dilawar Singh Date: Fri, 13 Mar 2020 18:52:19 +0530 Subject: [PATCH 16/16] Added some more tests. Basically parsing NML2 files. Whether they work or not will be known only after LEMS is implemented. --- python/moose/neuroml2/__main__.py | 3 +- python/moose/neuroml2/reader.py | 57 ++++++++++--- tests/support/Makefile | 5 ++ tests/support/nml2moose.xsl | 41 ++++++++++ .../nml_files/NML2_AnalogSynapsesHH.nml | 2 +- tests/support/nml_files/NML2_PyNNCells.nml | 79 ------------------- tests/support/test_nml2.py | 6 +- 7 files changed, 95 insertions(+), 98 deletions(-) create mode 100644 tests/support/Makefile create mode 100644 tests/support/nml2moose.xsl delete mode 100644 tests/support/nml_files/NML2_PyNNCells.nml diff --git a/python/moose/neuroml2/__main__.py b/python/moose/neuroml2/__main__.py index daafc2312b..274fba633c 100644 --- a/python/moose/neuroml2/__main__.py +++ b/python/moose/neuroml2/__main__.py @@ -21,12 +21,11 @@ def _addStreamer(): def main(**kwargs): logger_.info("Reading file %s" % kwargs['nml2file']) moose.mooseReadNML2(kwargs['nml2file']) - _addStreamer() - moose.reinit() simtime = kwargs['runtime'] moose.start(simtime) + logger_.info("All done") if __name__ == '__main__': import argparse diff --git a/python/moose/neuroml2/reader.py b/python/moose/neuroml2/reader.py index 33075754db..ce8e9f251e 100644 --- a/python/moose/neuroml2/reader.py +++ b/python/moose/neuroml2/reader.py @@ -123,11 +123,9 @@ def _findCaConc(): This is a hack, though it is likely to work in most cases. """ caConcs = moose.wildcardFind("/library/##[TYPE=CaConc]") - assert ( - len(caConcs) == 1 - ), "No moose.CaConc found. Currently moose \ - supports HHChannel which depends only on moose.CaConc ." - + assert len(caConcs) == 1, "No moose.CaConc found." + \ + " Currently moose supports HHChannel which depends only " + \ + " on moose.CaConc. %s" % str(caConcs) return caConcs[0] @@ -288,6 +286,22 @@ def getCellInPopulation(self, pop_id, index): return self.cells_in_populations[pop_id][index] def getComp(self, pop_id, cellIndex, segId): + if pop_id not in self.pop_to_cell_type: + logger_.error("%s is not in populations: %s" % (pop_id + , str(list(self.pop_to_cell_type.keys())))) + raise LookupError("%s not found" % pop_id) + + cellType = self.pop_to_cell_type[pop_id] + if cellType not in self.seg_id_to_comp_name: + logger_.error("%s not found in %s.compartments: %s" % (cellType + , pop_id, str(list(self.seg_id_to_comp_name.keys())))) + raise LookupError("%s not found" % cellType) + + compt = self.seg_id_to_comp_name[cellType] + if segId not in compt: + logger_.error("%s not found in %s.%s.segments: %s" % (compt + , pop_id, cellType, str(list(compt.keys())))) + raise LookupError("%s not found" % segId) return moose.element( "%s/%s/%s/%s" % ( @@ -300,15 +314,29 @@ def getComp(self, pop_id, cellIndex, segId): def createPopulations(self): for pop in self.network.populations: + # Sometime NML2 returns None instead of 0 + logger_.info("Adding population %s" % pop) + pop.size = 0 if pop.size is None else pop.size mpop = moose.Neutral("%s/%s" % (self.lib.path, pop.id)) self.cells_in_populations[pop.id] = {} + + # Either population have size of instances for i in range(pop.size): - logger_.info("Creating (%d out of %d) instances of %s (Type %s) under %s" - % (i, pop.size, pop.id, pop.component, mpop) - ) self.pop_to_cell_type[pop.id] = pop.component chid = moose.copy(self.proto_cells[pop.component], mpop, "%d"%i) self.cells_in_populations[pop.id][i] = chid + logger_.info("Created %s instances of %s (Type %s)" + % (chid, pop.id, pop.component) + ) + + # Add instance of population. + for i, instance in enumerate(pop.instances): + self.pop_to_cell_type[pop.id] = pop.component + chid = moose.copy(self.proto_cells[pop.component], mpop, '%d'%instance.id) + self.cells_in_populations[pop.id][instance.id] = chid + logger_.info("Created %s instances of %s (Type %s)" + % (chid, pop.id, pop.component) + ) def getInput(self, input_id): return moose.element("%s/inputs/%s" % (self.lib.path, input_id)) @@ -349,16 +377,16 @@ def createIAFCellPrototype(self, iaf): mLIF.Rm = 1.0/SI(iaf.leak_conductance) if hasattr(iaf, 'leak_reversal'): - logger_.warning("moose.LIF does not supprot leakReversal") + logger_.warning("moose.LIF does not supprot 'leakReversal' ") self.proto_cells[iaf.id] = mLIF self.nml_cells_to_moose[iaf.id] = mLIF self.moose_to_nml[mLIF] = iaf + # IAF cells have no morphology. Only one element. We need to create an element which + # can recieve input. + self.seg_id_to_comp_name[iaf.id] = {0:''} - quit() - # self.createMorphology(cell, nrn, symmetric=symmetric) - # self.importBiophysics(cell, nrn) return iaf, mLIF def createCellPrototype(self, cell, symmetric=True): @@ -378,7 +406,12 @@ def createMorphology(self, nmlcell, moosecell, symmetric=True): """ morphology = nmlcell.morphology + if morphology is None: + logger_.warning("%s has no morphology?" % nmlcell) + return + segments = morphology.segments + id_to_segment = dict([(seg.id, seg) for seg in segments]) if symmetric: compclass = moose.SymCompartment diff --git a/tests/support/Makefile b/tests/support/Makefile new file mode 100644 index 0000000000..52f539e3df --- /dev/null +++ b/tests/support/Makefile @@ -0,0 +1,5 @@ +all : test_nml2moose + + +test_nml2moose : ./nml2moose.xsl + xsltproc $< ./nml_files/NML2_FullCell.nml diff --git a/tests/support/nml2moose.xsl b/tests/support/nml2moose.xsl new file mode 100644 index 0000000000..bab98efe3d --- /dev/null +++ b/tests/support/nml2moose.xsl @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + " + + + :[] + + + + + + + + + + + + + + diff --git a/tests/support/nml_files/NML2_AnalogSynapsesHH.nml b/tests/support/nml_files/NML2_AnalogSynapsesHH.nml index d311651f06..75db6c4c6e 100644 --- a/tests/support/nml_files/NML2_AnalogSynapsesHH.nml +++ b/tests/support/nml_files/NML2_AnalogSynapsesHH.nml @@ -37,7 +37,7 @@ - + diff --git a/tests/support/nml_files/NML2_PyNNCells.nml b/tests/support/nml_files/NML2_PyNNCells.nml deleted file mode 100644 index ea50b4c5ec..0000000000 --- a/tests/support/nml_files/NML2_PyNNCells.nml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/tests/support/test_nml2.py b/tests/support/test_nml2.py index f6a0daf773..d9b77df029 100644 --- a/tests/support/test_nml2.py +++ b/tests/support/test_nml2.py @@ -204,16 +204,14 @@ def test_nml2_jkopsick(): def test_parse_nml_files(): import glob - files = glob.glob(os.path.join(SCRIPT_DIR, 'nml_files', '*.nml')) + files = glob.glob(os.path.join(SCRIPT_DIR, 'nml_files', 'NML2_*.nml')) print("Total %s files found" % len(files)) for f in files: if moose.exists('/model'): moose.delete('/model') if moose.exists('/library'): moose.delete('/library') - print("\n\n=========================================") + print("=========================================") print("[INFO ] Reading file %s" % f) moose.mooseReadNML2(f) - - quit() def main():