diff --git a/moose-core/.travis.yml b/moose-core/.travis.yml index c4aa5e91786a7e86eee00c2f24b8972b7a4bc1be..2f37437c30ae8d941c6062a7d09d5c11eaf0add4 100644 --- a/moose-core/.travis.yml +++ b/moose-core/.travis.yml @@ -1,16 +1,12 @@ language: cpp sudo: required -group: travis_lts -dist: xenial - -os: - - linux - - osx matrix: include: + - os : linux + dist: xenial - os : osx - osx_image: xcode10 + osx_image: xcode10.1 notifications: email: diff --git a/moose-core/.travis/travis_build_linux.sh b/moose-core/.travis/travis_build_linux.sh index 6885c7c61dc43cbf34ac4d07bae34ccd9bfca3b4..de62f8e5df3437019f972fd56a85109123243380 100755 --- a/moose-core/.travis/travis_build_linux.sh +++ b/moose-core/.travis/travis_build_linux.sh @@ -45,7 +45,7 @@ echo "Currently in `pwd`" ( mkdir -p _GSL_BUILD && cd _GSL_BUILD cmake -DDEBUG=ON -DPYTHON_EXECUTABLE="$PYTHON2" .. - $MAKE && ctest --output-on-failure + $MAKE && ctest --output-on-failure -E ".*socket_streamer.*" sudo make install && cd /tmp $PYTHON2 -c 'import moose;print(moose.__file__);print(moose.version())' ) @@ -58,16 +58,16 @@ if type $PYTHON3 > /dev/null; then sudo apt-get install -qq python3-networkx || echo "Error with apt" # GSL. ( - mkdir -p _GSL_BUILD2 && cd _GSL_BUILD2 && \ - cmake -DPYTHON_EXECUTABLE="$PYTHON3" -DDEBUG=ON .. - $MAKE && ctest --output-on-failure + mkdir -p _GSL_BUILD_PY3 && cd _GSL_BUILD_PY3 && \ + cmake -DWITH_NSDF=ON -DPYTHON_EXECUTABLE="$PYTHON3" -DDEBUG=ON .. + $MAKE && ctest --output-on-failure -E ".*socket_streamer.*" ) # BOOST ( - mkdir -p _BOOST_BUILD2 && cd _BOOST_BUILD2 && \ - cmake -DWITH_BOOST_ODE=ON -DPYTHON_EXECUTABLE="$PYTHON3" .. - $MAKE && ctest --output-on-failure + mkdir -p _BOOST_BUILD_PY3 && cd _BOOST_BUILD_PY3 && \ + cmake -DWITH_NSDF=ON -DWITH_BOOST_ODE=ON -DPYTHON_EXECUTABLE="$PYTHON3" .. + $MAKE && ctest --output-on-failure -E ".*socket_streamer.*" ) echo "All done" else diff --git a/moose-core/.travis/travis_build_osx.sh b/moose-core/.travis/travis_build_osx.sh index 7e947674b4f76968bd9ce53b24597bb0d6462161..bb1fffe0edf851e6cb14dfa32b00470dff05de0b 100755 --- a/moose-core/.travis/travis_build_osx.sh +++ b/moose-core/.travis/travis_build_osx.sh @@ -35,14 +35,14 @@ set -e && cmake -DDEBUG=ON \ -DPYTHON_EXECUTABLE=`which python` .. make pylint -j3 - make && ctest --output-on-failure + make && ctest --output-on-failure -E ".*socket_streamer.*" cd .. # Now with boost. mkdir -p _BOOST_BUILD && cd _BOOST_BUILD \ && cmake -DWITH_BOOST_ODE=ON -DDEBUG=ON \ -DPYTHON_EXECUTABLE=`which python` .. - make && ctest --output-on-failure + make && ctest --output-on-failure -E ".*socket_streamer.*" cd .. set +e diff --git a/moose-core/.travis/travis_prepare_linux.sh b/moose-core/.travis/travis_prepare_linux.sh index 37f5dd33ae01777ad31ef011a020cfa5c4e241d3..49afa941d2fa0f2d6c3af823166214dbbb0978cd 100755 --- a/moose-core/.travis/travis_prepare_linux.sh +++ b/moose-core/.travis/travis_prepare_linux.sh @@ -27,11 +27,14 @@ apt-get install -qq python3-lxml python-lxml apt-get install -qq python3-numpy python3-matplotlib python3-dev apt-get install -qq python-pip python3-pip apt-get install -qq python-tk python3-tk -apt-get install -qq libgraphviz-dev +apt-get install -qq libgraphviz-dev # Gsl apt-get install -qq libgsl0-dev || apt-get install -qq libgsl-dev +# hdf5 +apt-get install -qq libhdf5-serial-dev + # Boost related. apt-get install -qq liblapack-dev apt-get install -qq libboost-all-dev diff --git a/moose-core/CMakeLists.txt b/moose-core/CMakeLists.txt index b30ca55de3a35154d864570dbc974bf413b51c1b..0fa33eb6051be4169d0e3182c1f976a36cfbb95e 100644 --- a/moose-core/CMakeLists.txt +++ b/moose-core/CMakeLists.txt @@ -181,72 +181,6 @@ if(WITH_BOOST_ODE) add_definitions(-DUSE_BOOST_ODE -UUSE_GSL) endif() -# NSDF5 support. Disabled by default. -if(WITH_NSDF) - find_package(HDF5 COMPONENTS CXX HL) - if(NOT HDF5_FOUND) - message( - "==================================================================\n" - " HDF5 not found. Disabling NSDF support.\n\n" - " If you need NSDF support, please install hdf5-dev or hdf5-devel\n" - " package or equivalent.\n\n" - " $ sudo apt-get install libhdf5-dev \n" - " $ sudo yum install libhdf5-devel \n" - " $ brew install hdf5 \n\n" - " Otherwise, continue with 'make' and 'make install' \n" - " If you install hdf5 to non-standard path, export environment \n" - " variable HDF5_ROOT to the location. Rerun cmake \n" - "================================================================ \n" - ) - endif(NOT HDF5_FOUND) - - if(HDF5_FOUND) - include_directories( ${HDF5_INCLUDE_DIRS} ) - add_definitions( -DUSE_HDF5 -DENABLE_NSDF ) - if(HDF5_USE_STATIC_LIBRARIES) - message(STATUS "Finding static HDF5 libraries in $ENV{HDF5_ROOT}") - find_library(HDF5_CXX_LIBRARIES NAMES libhdf5.a - PATHS $ENV{HDF5_ROOT}/lib $ENV{HDF5_ROOT}/lib64 - ) - find_library(HDF5_HL_LIBRARIES NAMES libhdf5_hl.a - PATHS $ENV{HDF5_ROOT}/lib $ENV{HDF5_ROOT}/lib64 - ) - set(HDF5_LIBRARIES ${HDF5_CXX_LIBRARIES} ${HDF5_HL_LIBRARIES}) - endif() - - - # Make sure, HDF5_HL_LIBRARIES are set. The COMPONENTS in find_package may - # or may not work. See BhallaLab/moose-core#163. - if(NOT HDF5_HL_LIBRARIES) - set(HDF5_HL_LIBRARIES ${HDF5_HL_LIBRARIES}) - endif(NOT HDF5_HL_LIBRARIES) - list(APPEND HDF5_LIBRARIES ${HDF5_HL_LIBRARIES}) - - # message(STATUS "MOOSE will use following HDF5 ${HDF5_LIBRARIES}" ) - foreach(HDF5_LIB ${HDF5_LIBRARIES}) - if(HDF5_LIB) - get_filename_component( HDF5_LIB_EXT ${HDF5_LIB} EXT ) - if(HDF5_LIB_EXT) - if(${HDF5_LIB_EXT} STREQUAL ".a") - list(APPEND STATIC_LIBRARIES ${HDF5_LIB} ) - else( ) - list(APPEND SYSTEM_SHARED_LIBS ${HDF5_LIB} ) - endif( ) - endif() - endif( ) - endforeach( ) - else( HDF5_FOUND ) - message(STATUS "HDF5 is not found" ) - endif( HDF5_FOUND ) - # This is a fix for new HDF5 package on Debian/Ubuntu which installs hdf5 - # headers in non-standard path. issue #80. - if(HDF5_LIBRARY_DIRS) - set_target_properties( libmoose PROPERTIES LINK_FLAGS "-L${HDF5_LIBRARY_DIRS}" ) - endif() -else(WITH_NSDF) - message(STATUS "NSDF support is disabled" ) -endif(WITH_NSDF) - # Openmpi if(WITH_MPI) find_package(MPI REQUIRED) diff --git a/moose-core/basecode/ObjId.cpp b/moose-core/basecode/ObjId.cpp index 9c1c823139e778920457ddc6093d4b3d027aee6a..540bc4be70bee35c9de426ed5b4d651bafb36acb 100644 --- a/moose-core/basecode/ObjId.cpp +++ b/moose-core/basecode/ObjId.cpp @@ -112,8 +112,9 @@ bool ObjId::isOffNode() const char* ObjId::data() const { - return id.element()->data( id.element()->rawIndex( dataIndex ), - fieldIndex ); + return id.element()->data( + id.element()->rawIndex( dataIndex ), fieldIndex + ); } string ObjId::path() const diff --git a/moose-core/basecode/header.h b/moose-core/basecode/header.h index 22cb6fc28c11963c8f2e8e23c1bf2f5c4027657b..97a8bdf450d84b419b27a5c930bac9e0b4d4c0ff 100644 --- a/moose-core/basecode/header.h +++ b/moose-core/basecode/header.h @@ -99,7 +99,6 @@ class Neutral; #include "SharedFinfo.h" #include "FieldElementFinfo.h" #include "FieldElement.h" -#include "../builtins/Streamer.h" #include "../shell/Neutral.h" diff --git a/moose-core/builtins/CMakeLists.txt b/moose-core/builtins/CMakeLists.txt index 3df4cd964824591929dcd158f7c87b01564dd66e..8b96a98ede778da45649ed00f064ff603e95576e 100644 --- a/moose-core/builtins/CMakeLists.txt +++ b/moose-core/builtins/CMakeLists.txt @@ -1,6 +1,52 @@ cmake_minimum_required(VERSION 2.8) include( ${CMAKE_CURRENT_SOURCE_DIR}/../CheckCXXCompiler.cmake ) +# NSDF5 support. Disabled by default. +if(WITH_NSDF) + find_package(HDF5 COMPONENTS CXX HL) + if(NOT HDF5_FOUND) + message( + "==================================================================\n" + " HDF5 not found. Disabling NSDF support.\n\n" + " If you need NSDF support, please install hdf5-dev or hdf5-devel\n" + " package or equivalent.\n\n" + " $ sudo apt-get install libhdf5-dev \n" + " $ sudo yum install libhdf5-devel \n" + " $ brew install hdf5 \n\n" + " Otherwise, continue with 'make' and 'make install' \n" + " If you install hdf5 to non-standard path, export environment \n" + " variable HDF5_ROOT to the location. Rerun cmake \n" + "================================================================ \n" + ) + endif(NOT HDF5_FOUND) + + if(HDF5_FOUND) + include_directories(${HDF5_CXX_INCLUDE_DIRS} ) + add_definitions(-DUSE_HDF5 -DENABLE_NSDF ) + add_definitions(${HDF5_CXX_DEFINITIONS}) + if(HDF5_USE_STATIC_LIBRARIES) + message(STATUS "Finding static HDF5 libraries in $ENV{HDF5_ROOT}") + find_library(HDF5_CXX_LIBRARIES NAMES libhdf5.a + PATHS $ENV{HDF5_ROOT}/lib $ENV{HDF5_ROOT}/lib64 + ) + find_library(HDF5_HL_LIBRARIES NAMES libhdf5_hl.a + PATHS $ENV{HDF5_ROOT}/lib $ENV{HDF5_ROOT}/lib64 + ) + set(HDF5_LIBRARIES ${HDF5_CXX_LIBRARIES} ${HDF5_HL_LIBRARIES}) + endif() + + # Make sure, HDF5_HL_LIBRARIES are set. The COMPONENTS in find_package may + # or may not work. See BhallaLab/moose-core#163. + if(NOT HDF5_HL_LIBRARIES) + set(HDF5_HL_LIBRARIES ${HDF5_HL_LIBRARIES}) + endif(NOT HDF5_HL_LIBRARIES) + list(APPEND HDF5_LIBRARIES ${HDF5_HL_LIBRARIES}) + + else(HDF5_FOUND) + message(STATUS "HDF5 is not found. Disabling NSDF support." ) + endif( HDF5_FOUND ) +endif(WITH_NSDF) + set(SRCS Arith.cpp Group.cpp @@ -33,3 +79,6 @@ if(WITH_NSDF AND HDF5_FOUND) endif() add_library(moose_builtins ${SRCS} ) +if(WITH_NSDF AND HDF5_FOUND) + target_link_libraries(moose_builtins ${HDF5_CXX_LIBRARIES} ${HDF5_HL_LIBRARIES}) +endif() diff --git a/moose-core/builtins/MooseSocketInfo.h b/moose-core/builtins/MooseSocketInfo.h new file mode 100644 index 0000000000000000000000000000000000000000..9e825eec3c3719751421243dd728a7b58ebfb6af --- /dev/null +++ b/moose-core/builtins/MooseSocketInfo.h @@ -0,0 +1,83 @@ +/* + * Description: Utility class for handling socket. + * Author: Dilawar Singh (), dilawars@ncbs.res.in + * Organization: NCBS Bangalore + */ + +#ifndef MOOSESOCKETINFO_H +#define MOOSESOCKETINFO_H + +typedef enum t_socket_type_ {TCP_SOCKET, UNIX_DOMAIN_SOCKET} SocketType; // Type of socket. + +class MooseSocketInfo +{ +public: + MooseSocketInfo( const string& addr = "" ) + : address(addr), valid(false) + { + if( addr.size() > 0 ) + init(); + } + + ~MooseSocketInfo() { ; } + + void setAddress( const string addr ) + { + address = addr; + init(); + } + + void init( void ) + { + if( "file://" == address.substr(0, 7)) + { + type = UNIX_DOMAIN_SOCKET; + filepath = address.substr(7); + valid = true; + } + else if( "http://" == address.substr(0, 7)) + { + type = TCP_SOCKET; + auto colPos = address.find_last_of(':'); + if( colPos == string::npos ) + { + port = 0; + host = address; + } + else + { + host = address.substr(0, colPos); + port = std::stoi(address.substr(colPos+1)); + } + valid = true; + } + else if( '/' == address[0] ) + { + type = UNIX_DOMAIN_SOCKET; + filepath = address; + valid = true; + } + } + + // C++ is so stupid: https://stackoverflow.com/q/476272/1805129 + friend std::ostream& operator<<(ostream& os, const MooseSocketInfo& info) + { + os << "ADDRESS= " << info.address << " TYPE= " << info.type; + if( info.type == UNIX_DOMAIN_SOCKET) + os << ". FILEPATH " << info.filepath; + else + os << ". HOST:PORT=" << info.host << ":" << info.port; + return os; + } + +public: + SocketType type; + string address; + string filepath; + string host; + bool valid; + size_t port; +}; + + +#endif /* end of include guard: MOOSESOCKETINFO_H */ diff --git a/moose-core/builtins/SocketStreamer.cpp b/moose-core/builtins/SocketStreamer.cpp index e3097f088bd2b7f99d52743a52ce249ff7d01141..38cbae5e099bb1132ff8deeb02c534f096909be5 100644 --- a/moose-core/builtins/SocketStreamer.cpp +++ b/moose-core/builtins/SocketStreamer.cpp @@ -66,25 +66,25 @@ const Cinfo* SocketStreamer::initCinfo() static DestFinfo addTable( "addTable" , "Add a table to SocketStreamer" - , new OpFunc1<SocketStreamer, Id>(&SocketStreamer::addTable) + , new OpFunc1<SocketStreamer, ObjId>(&SocketStreamer::addTable) ); static DestFinfo addTables( "addTables" , "Add many tables to SocketStreamer" - , new OpFunc1<SocketStreamer, vector<Id> >(&SocketStreamer::addTables) + , new OpFunc1<SocketStreamer, vector<ObjId> >(&SocketStreamer::addTables) ); static DestFinfo removeTable( "removeTable" , "Remove a table from SocketStreamer" - , new OpFunc1<SocketStreamer, Id>(&SocketStreamer::removeTable) + , new OpFunc1<SocketStreamer, ObjId>(&SocketStreamer::removeTable) ); static DestFinfo removeTables( "removeTables" , "Remove tables -- if found -- from SocketStreamer" - , new OpFunc1<SocketStreamer, vector<Id>>(&SocketStreamer::removeTables) + , new OpFunc1<SocketStreamer, vector<ObjId>>(&SocketStreamer::removeTables) ); /*----------------------------------------------------------------------------- @@ -134,12 +134,9 @@ static const Cinfo* tableStreamCinfo = SocketStreamer::initCinfo(); SocketStreamer::SocketStreamer() : currTime_(0.0) , numMaxClients_(1) - , sockType_ ( UNIX_DOMAIN_SOCKET ) , sockfd_(-1) , clientfd_(-1) - , ip_( TCP_SOCKET_IP ) - , port_( TCP_SOCKET_PORT ) - , address_ ( "file://MOOSE" ) + , sockInfo_( MooseSocketInfo( "file://MOOSE" ) ) { clk_ = reinterpret_cast<Clock*>( Id(1).eref().data() ); @@ -150,7 +147,6 @@ SocketStreamer::SocketStreamer() : tableIds_.resize(0); tableTick_.resize(0); tableDt_.resize(0); - } SocketStreamer& SocketStreamer::operator=( const SocketStreamer& st ) @@ -167,36 +163,29 @@ SocketStreamer::~SocketStreamer() if(sockfd_ > 0) { LOG(moose::debug, "Closing socket " << sockfd_ ); - shutdown(sockfd_, SHUT_RD); + shutdown(sockfd_, SHUT_RDWR); close(sockfd_); - if( sockType_ == UNIX_DOMAIN_SOCKET ) - ::unlink( unixSocketFilePath_.c_str() ); + if( sockInfo_.type == UNIX_DOMAIN_SOCKET ) + ::unlink( sockInfo_.filepath.c_str() ); } - if( processThread_.joinable() ) + if( processThread_.joinable()) processThread_.join(); -} -/* --------------------------------------------------------------------------*/ -/** - * @Synopsis Stop a thread. - * See: http://www.bo-yang.net/2017/11/19/cpp-kill-detached-thread - * - * @Param tname name of thread. - */ -/* ----------------------------------------------------------------------------*/ -//void SocketStreamer::stopThread(const std::string& tname) -//{ -// ThreadMap::const_iterator it = tm_.find(tname); -// if (it != tm_.end()) -// { -// it->second.std::thread::~thread(); // thread not killed -// tm_.erase(tname); -// LOG(moose::debug, "Thread " << tname << " killed." ); -// } -//} + // Close the client as well. + if( clientfd_ > -1 ) + { + shutdown(clientfd_, SHUT_RDWR); + close(clientfd_); + } +} +void SocketStreamer::addStringToDoubleVec(vector<double>&res, const string s) +{ + for(char c : s) + res.push_back((double)c); +} /* --------------------------------------------------------------------------*/ /** @@ -217,17 +206,16 @@ void SocketStreamer::listenToClients(size_t numMaxClients) void SocketStreamer::initServer( void ) { - setSocketType( ); - if( sockType_ == UNIX_DOMAIN_SOCKET ) + if( sockInfo_.type == UNIX_DOMAIN_SOCKET ) initUDSServer(); else initTCPServer(); - LOG(moose::debug, "Successfully created SocketStreamer server: " << sockfd_); + LOG(moose::info, "Successfully initialized streamer socket: " << sockfd_); // Listen for incoming clients. This function does nothing if connection is // already made. - listenToClients(2); + listenToClients(1); } void SocketStreamer::configureSocketServer( ) @@ -236,7 +224,7 @@ void SocketStreamer::configureSocketServer( ) // for details. We are making it 'reusable'. int on = 1; -#ifdef SO_REUSEADDR +#ifdef SO_REUSEPORT if(0 > setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT, (const char *)&on, sizeof(on))) LOG(moose::warning, "Warn: setsockopt() failed"); #endif @@ -257,30 +245,39 @@ void SocketStreamer::initUDSServer( void ) if( sockfd_ > 0 ) { - unixSocketFilePath_ = address_.substr(7); bzero(&sockAddrUDS_, sizeof(sockAddrUDS_)); + bzero(&sockAddrUDS_, sizeof(sockAddrUDS_)); sockAddrUDS_.sun_family = AF_UNIX; - strncpy(sockAddrUDS_.sun_path, unixSocketFilePath_.c_str(), sizeof(sockAddrUDS_.sun_path)-1); + strncpy(sockAddrUDS_.sun_path, sockInfo_.filepath.c_str(), sizeof(sockAddrUDS_.sun_path)-1); configureSocketServer(); // Bind. Make sure bind is not std::bind if(0 > ::bind(sockfd_, (struct sockaddr*) &sockAddrUDS_, sizeof(sockAddrUDS_))) { isValid_ = false; - LOG(moose::warning, "Warn: Failed to create socket at " << unixSocketFilePath_ + LOG(moose::warning, "Warn: Failed to create socket at " << sockInfo_.filepath << ". File descriptor: " << sockfd_ << ". Erorr: " << strerror(errno) ); } + + if(! moose::filepath_exists(sockInfo_.filepath)) + { + LOG( moose::warning, "No file " << sockInfo_.filepath << " exists." ); + isValid_ = false; + } } if( (! isValid_) || (sockfd_ < 0) ) - ::unlink( unixSocketFilePath_.c_str() ); + { + LOG( moose::warning, "Failed to create socket : " << sockInfo_ ); + ::unlink( sockInfo_.filepath.c_str() ); + } } void SocketStreamer::initTCPServer( void ) { // Create a blocking socket. - LOG( moose::debug, "Creating TCP socket on port: " << port_ ); + LOG( moose::debug, "Creating TCP socket on port: " << sockInfo_.port ); sockfd_ = socket(AF_INET, SOCK_STREAM, 0); if( 0 > sockfd_ ) { @@ -293,122 +290,137 @@ void SocketStreamer::initTCPServer( void ) bzero((char*) &sockAddrTCP_, sizeof(sockAddrTCP_)); sockAddrTCP_.sin_family = AF_INET; sockAddrTCP_.sin_addr.s_addr = INADDR_ANY; - sockAddrTCP_.sin_port = htons( port_ ); + sockAddrTCP_.sin_port = htons( sockInfo_.port ); // Bind. Make sure bind is not std::bind if(0 > ::bind(sockfd_, (struct sockaddr*) &sockAddrTCP_, sizeof(sockAddrTCP_))) { isValid_ = false; - LOG(moose::warning, "Warn: Failed to create server at " << ip_ << ":" << port_ - << ". File descriptor: " << sockfd_ - << ". Erorr: " << strerror(errno) + LOG(moose::warning, "Warn: Failed to create server at " + << sockInfo_.host << ":" << sockInfo_.port + << ". File descriptor: " << sockfd_ + << ". Erorr: " << strerror(errno) ); return; } } - /* --------------------------------------------------------------------------*/ /** - * @Synopsis Stream data over socket. + * @Synopsis Convert data to JSON. * - * @Returns True of success, false otherwise. It is callee job to clean up data_ - * on a successful return from this function. + * @Returns JSON representation. */ /* ----------------------------------------------------------------------------*/ -bool SocketStreamer::streamData( ) +void SocketStreamer::dataToStream(map<string, vector<double>>& data) { - if( clientfd_ > 0) + for( size_t i = 0; i < tables_.size(); i++) { - buffer_ += dataToString(); - - if( buffer_.size() < frameSize_ ) - buffer_ += string(frameSize_-buffer_.size(), ' '); - - string toSend = buffer_.substr(0, frameSize_); - - int sent = send(clientfd_, buffer_.substr(0, frameSize_).c_str(), frameSize_, MSG_MORE); - buffer_ = buffer_.erase(0, sent); - - assert( sent == (int)frameSize_); - LOG(moose::debug, "Sent " << sent << " bytes." ); - - if(0 > sent) - { - LOG(moose::warning, "Failed to send. Error: " << strerror(errno) - << ". client id: " << clientfd_ ); - return false; - } - - // clear up the tables. - for( auto t : tables_ ) - t->clearVec(); - return true; + vector<double> vec; + tables_[i]->collectData(vec, true, false); + if( ! vec.empty() ) + data[columns_[i+1]] = vec; } - else - LOG(moose::warning, "No client found to stream data. ClientFD: " << clientfd_ ); - - return false; } /* --------------------------------------------------------------------------*/ /** - * @Synopsis Convert table to string (use scientific notation). + * @Synopsis Stream data over socket. * - * @Returns String in JSON like format. + * @Returns True of success, false otherwise. It is callee job to clean up data_ + * on a successful return from this function. */ /* ----------------------------------------------------------------------------*/ -string SocketStreamer::dataToString( ) +int SocketStreamer::streamData( ) { - stringstream ss; - // Enabling this would be require quite a lot of characters to be streamed. - //ss.precision( 7 ); - //ss << std::scientific; - vector<double> data; - - // Else stream the data. - ss << "{"; - for( size_t i = 0; i < tables_.size(); i++) + map<string, vector<double>> data; + dataToStream(data); + + if(data.empty()) + { + LOG(moose::debug, "No data in tables."); + return 0; + } + + // Construct a void* array to send over the socket. Serialize the data. + // e.g. H 10 / a / t a b l e / a V 4 0.1 0.2 0.3 0.2 + // H => Header start (chars) + // V => Value starts (double) + for(auto v: data) { - ss << "\"" << columns_[i+1] << "\":["; - ss << tables_[i]->toJSON(true); - ss << "],"; + // First header. + vecToStream_.push_back((double)'H'); + vecToStream_.push_back(v.first.size()); + addStringToDoubleVec(vecToStream_, v.first); + + // Now data. + vecToStream_.push_back((double)'V'); + vecToStream_.push_back(v.second.size()); + vecToStream_.insert(vecToStream_.end(), v.second.begin(), v.second.end()); } - // remove , at the end else it won't be a valid JSON. - string res = ss.str(); - if( ',' == res.back()) - res.pop_back(); - res += "}\n"; - return res; + size_t dtypeSize = sizeof(double); + int sent = send(clientfd_, (void*) &vecToStream_[0], dtypeSize*vecToStream_.size(), MSG_MORE); + LOG(moose::debug, "Sent " << sent << " bytes." ); + if( sent < 0 ) + return errno; + + vecToStream_.erase(vecToStream_.begin(), vecToStream_.begin()+(sent/dtypeSize)); + return 0; } + bool SocketStreamer::enoughDataToStream(size_t minsize) { for( size_t i = 0; i < tables_.size(); i++) - if(tables_[i]->getVec().size() >= minsize ) + if(tables_[i]->getVec().size() >= minsize) return true; return false; } -void SocketStreamer::connectAndStream( ) +void SocketStreamer::connect( ) { - currTime_ = clk_->getCurrentTime(); - // If server was invalid then there is no point. if( ! isValid_ ) - { - LOG( moose::error, "Server could not set up." ); return; + + struct sockaddr_storage clientAddr; + socklen_t addrLen = sizeof(clientAddr); + clientfd_ = ::accept(sockfd_,(struct sockaddr*) &clientAddr, &addrLen); + assert( clientfd_ ); + + char ipstr[INET6_ADDRSTRLEN]; + int port = -1; + // deal with both IPv4 and IPv6: + if (clientAddr.ss_family == AF_INET) + { + struct sockaddr_in *s = (struct sockaddr_in *)&clientAddr; + port = ntohs(s->sin_port); + inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); + } + else + { // AF_INET6 + struct sockaddr_in6 *s = (struct sockaddr_in6 *)&clientAddr; + port = ntohs(s->sin6_port); + inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr); } - // Now lets get into a loop to stream data. - while( (! all_done_) ) + LOG(moose::info, "Connected to " << ipstr << ':' << port); + return; +} + +void SocketStreamer::stream( void ) +{ + if(clientfd_ > 0) { - clientfd_ = ::accept(sockfd_, NULL, NULL); - if( clientfd_ >= 0 ) - streamData(); + if( EPIPE == streamData() ) + { + LOG( moose::warning, "Broken pipe. Couldn't stream." ); + return; + } } + else + LOG( moose::warning, "No client." ); } /** @@ -443,7 +455,10 @@ void SocketStreamer::reinit(const Eref& e, ProcPtr p) // Launch a thread in background which monitors the any client trying to // make connection to server. - processThread_ = std::thread(&SocketStreamer::connectAndStream, this); + processThread_ = std::thread(&SocketStreamer::connect, this); + + // NOw introduce some delay. + timeStamp_ = std::chrono::high_resolution_clock::now(); } /** @@ -454,8 +469,10 @@ void SocketStreamer::reinit(const Eref& e, ProcPtr p) */ void SocketStreamer::process(const Eref& e, ProcPtr p) { - // It does nothing. See the connectAndStream function. - ; + // processTickMicroSec = std::chrono::duration_cast<std::chrono::microseconds>( + // std::chrono::high_resolution_clock::now() - timeStamp_).count(); + // timeStamp_ = std::chrono::high_resolution_clock::now(); + stream(); } /** @@ -463,7 +480,7 @@ void SocketStreamer::process(const Eref& e, ProcPtr p) * * @param table Id of table. */ -void SocketStreamer::addTable( Id table ) +void SocketStreamer::addTable( ObjId table ) { // If this table is not already in the vector, add it. for( size_t i = 0; i < tableIds_.size(); i++) @@ -488,29 +505,21 @@ void SocketStreamer::addTable( Id table ) * * @param tables */ -void SocketStreamer::addTables( vector<Id> tables ) -{ - if( tables.size() == 0 ) - return; - for( vector<Id>::const_iterator it = tables.begin(); it != tables.end(); it++) - addTable( *it ); -} - void SocketStreamer::addTables( vector<ObjId> tables ) { if( tables.size() == 0 ) return; - for( auto t : tables ) - addTable( Id(t) ); -} + for(auto t : tables) + addTable(t); +} /** * @brief Remove a table from SocketStreamer. * * @param table. Id of table. */ -void SocketStreamer::removeTable( Id table ) +void SocketStreamer::removeTable( ObjId table ) { int matchIndex = -1; for (size_t i = 0; i < tableIds_.size(); i++) @@ -530,32 +539,14 @@ void SocketStreamer::removeTable( Id table ) } } -/* --------------------------------------------------------------------------*/ -/** - * @Synopsis Determines socket type from the given address. - */ -/* ----------------------------------------------------------------------------*/ -void SocketStreamer::setSocketType( ) -{ - LOG( moose::debug, "Socket address is " << address_ ); - if( "file://" == address_.substr(0, 7)) - sockType_ = UNIX_DOMAIN_SOCKET; - else if( "http://" == address_.substr(0,7)) - sockType_ = TCP_SOCKET; - else - sockType_ = UNIX_DOMAIN_SOCKET; - return; -} - /** * @brief Remove multiple tables -- if found -- from SocketStreamer. * * @param tables */ -void SocketStreamer::removeTables( vector<Id> tables ) +void SocketStreamer::removeTables( vector<ObjId> tables ) { - for( vector<Id>::const_iterator it = tables.begin(); it != tables.end(); it++) - removeTable( *it ); + for(auto &t: tables) removeTable(t); } /** @@ -571,21 +562,20 @@ size_t SocketStreamer::getNumTables( void ) const void SocketStreamer::setPort( const size_t port ) { - port_ = port; + sockInfo_.port = port; } size_t SocketStreamer::getPort( void ) const { - assert( port_ > 1 ); - return port_; + return sockInfo_.port; } void SocketStreamer::setAddress( const string addr ) { - address_ = addr; + sockInfo_.setAddress(addr); } string SocketStreamer::getAddress( void ) const { - return address_; + return sockInfo_.address; } diff --git a/moose-core/builtins/SocketStreamer.h b/moose-core/builtins/SocketStreamer.h index c52bb7dd4d1419895d16d7ba3f9ddc9cb331fa45..7e811c817b79cb22aa27534aeee8cc88bb8653c8 100644 --- a/moose-core/builtins/SocketStreamer.h +++ b/moose-core/builtins/SocketStreamer.h @@ -5,10 +5,9 @@ #ifndef SocketStreamer_INC #define SocketStreamer_INC -#define STRINGSTREAM_DOUBLE_PRECISION 10 - #include <iostream> #include <string> +#include <vector> #include <map> #include <fstream> #include <sstream> @@ -16,6 +15,7 @@ #include <atomic> #include "StreamerBase.h" +#include "MooseSocketInfo.h" #include "Table.h" // If cmake does not set it, use the default port. @@ -37,6 +37,7 @@ #include <sys/ioctl.h> #include <sys/un.h> #include <netinet/in.h> +#include <arpa/inet.h> // MSG_MORE is not defined in OSX. So stupid! #ifndef MSG_MORE @@ -45,10 +46,10 @@ using namespace std; -typedef enum t_socket_type_ {TCP_SOCKET, UNIX_DOMAIN_SOCKET} SocketType; // Type of socket. class Clock; + class SocketStreamer : public StreamerBase { @@ -75,6 +76,9 @@ public: // Make connection to client void listenToClients(size_t numMaxClients); + // Find minimum number of elements in tables. + pair<size_t, size_t> minMaxNumberOfElemementsInTables( ); + /* Cleaup before quitting */ void cleanUp( void ); @@ -84,28 +88,26 @@ public: size_t getPort( void ) const; void setPort( const size_t port ); - SocketType getSocketType( ); - void setSocketType(void); - /*----------------------------------------------------------------------------- * Streaming data. *-----------------------------------------------------------------------------*/ bool enoughDataToStream(size_t minsize=10); - bool streamData(); - void connectAndStream( ); + int streamData(); + // connect is monitored in a thread. + void connect( void ); + void stream(void); size_t getNumTables( void ) const; - void addTable( Id table ); - void addTables( vector<Id> tables); + void addTable( ObjId table ); void addTables( vector<ObjId> tables); - void removeTable( Id table ); - void removeTables( vector<Id> table ); + void removeTable( ObjId table ); + void removeTables( vector<ObjId> table ); - string dataToString(); + void dataToStream(map<string, vector<double>>& vec); - // void stopThread(const std::string& tname); + static void addStringToDoubleVec(vector<double>&res, const string s); /** Dest functions. * The process function called by scheduler on every tick @@ -119,6 +121,7 @@ public: static const Cinfo * initCinfo(); + private: // dt_ and tick number of Table's clock @@ -133,16 +136,9 @@ private: /* Socket related */ int numMaxClients_; - SocketType sockType_ = UNIX_DOMAIN_SOCKET; // Default is UNIX_DOMAIN_SOCKET int sockfd_; // socket file descriptor. int clientfd_; // client file descriptor - // TCP socket. - string ip_; // ip_ address of server. - unsigned short port_; // port number if socket is TCP_SOCKET - string address_; // adress of socket. Specified by user. - string unixSocketFilePath_; - // address holdder for TCP and UDS sockets. struct sockaddr_in sockAddrTCP_; struct sockaddr_un sockAddrUDS_; @@ -152,13 +148,18 @@ private: bool isValid_ = true; std::thread processThread_; string buffer_; + vector<double> vecToStream_; double thisDt_; - size_t frameSize_ = 2048; - // We need clk_ pointer for handling Clock* clk_ = nullptr; + // Socket Info + MooseSocketInfo sockInfo_; + + // How long it takes between process calls. + size_t processTickMicroSec; + std::chrono::high_resolution_clock::time_point timeStamp_; }; #endif /* ----- #ifndef SocketStreamer_INC ----- */ diff --git a/moose-core/builtins/Streamer.cpp b/moose-core/builtins/Streamer.cpp index c583be1fee2ee8cc00ec68c1f6ee8e4cef2a4293..aae926bc7e18ee42c0e8698761c627580d3ae2a0 100644 --- a/moose-core/builtins/Streamer.cpp +++ b/moose-core/builtins/Streamer.cpp @@ -1,17 +1,8 @@ /*** - * Filename: Streamer.cpp - * * Description: Stream table data. * - * Version: 0.0.1 - * Created: 2016-04-26 - - * Revision: none - * * Author: Dilawar Singh <dilawars@ncbs.res.in> * Organization: NCBS Bangalore - * - * License: GNU GPL2 */ #include <algorithm> @@ -45,12 +36,18 @@ const Cinfo* Streamer::initCinfo() , &Streamer::getFormat ); - static ReadOnlyValueFinfo< Streamer, size_t > numTables ( + static ReadOnlyValueFinfo<Streamer, size_t> numTables ( "numTables" , "Number of Tables handled by Streamer " , &Streamer::getNumTables ); + static ReadOnlyValueFinfo<Streamer, size_t> numWriteEvents( + "numWriteEvents" + , "Number of time streamer was called to write. (For debugging/performance reason only)" + , &Streamer::getNumWriteEvents + ); + /*----------------------------------------------------------------------------- * *-----------------------------------------------------------------------------*/ @@ -96,7 +93,7 @@ const Cinfo* Streamer::initCinfo() *-----------------------------------------------------------------------------*/ static Finfo* procShared[] = { - &process , &reinit , &addTable, &addTables, &removeTable, &removeTables + &process, &reinit, &addTable, &addTables, &removeTable, &removeTables }; static SharedFinfo proc( @@ -107,7 +104,7 @@ const Cinfo* Streamer::initCinfo() static Finfo * tableStreamFinfos[] = { - &outfile, &format, &proc, &numTables + &outfile, &format, &proc, &numTables, &numWriteEvents }; static string doc[] = @@ -140,8 +137,9 @@ Streamer::Streamer() { // Not all compilers allow initialization during the declaration of class // methods. - format_ = "npy"; - columns_.push_back( "time" ); /* First column is time. */ + format_ = "csv"; + numWriteEvents_ = 0; + columns_.push_back("time"); /* First column is time. */ tables_.resize(0); tableIds_.resize(0); tableTick_.resize(0); @@ -238,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_, "w", data_, columns_); data_.clear( ); } @@ -261,15 +259,11 @@ void Streamer::cleanUp( ) */ void Streamer::process(const Eref& e, ProcPtr p) { - //LOG( moose::debug, "Writing to table" ); + // LOG( moose::debug, "Writing Streamer data to file." ); zipWithTime( ); - - // Write only if there are more than 100 entry in first table. - if( tables_[0]->getVecSize() > 100 ) - { - StreamerBase::writeToOutFile( outfilePath_, format_, "a", data_, columns_ ); - data_.clear( ); - } + StreamerBase::writeToOutFile( outfilePath_, format_, "a", data_, columns_ ); + data_.clear(); + numWriteEvents_ += 1; } @@ -356,6 +350,19 @@ size_t Streamer::getNumTables( void ) const return tables_.size(); } +/* --------------------------------------------------------------------------*/ +/** + * @Synopsis Get number of write events in streamer. Useful for debugging and + * performance measuerments. + * + * @Returns + */ +/* ----------------------------------------------------------------------------*/ +size_t Streamer::getNumWriteEvents( void ) const +{ + return numWriteEvents_; +} + string Streamer::getOutFilepath( void ) const { diff --git a/moose-core/builtins/Streamer.h b/moose-core/builtins/Streamer.h index 0a06e547ac26227744727256a8579c1434159fe3..3136211fced1004b475911cd1f469e0f2daf95b6 100644 --- a/moose-core/builtins/Streamer.h +++ b/moose-core/builtins/Streamer.h @@ -49,6 +49,7 @@ public: void setFormat( string format ); size_t getNumTables( void ) const; + size_t getNumWriteEvents( void ) const; void addTable( Id table ); void addTables( vector<Id> tables); @@ -74,6 +75,9 @@ private: string outfilePath_; string format_; + + size_t numWriteEvents_; + bool isOutfilePathSet_; // dt_ and tick number of Table's clock diff --git a/moose-core/builtins/StreamerBase.cpp b/moose-core/builtins/StreamerBase.cpp index e6d590be0fe1b2a06a2c242946e4814a32a8ec38..902c3f24c765f98e1a9df9fc3554f09d689777e7 100644 --- a/moose-core/builtins/StreamerBase.cpp +++ b/moose-core/builtins/StreamerBase.cpp @@ -91,14 +91,14 @@ void StreamerBase::writeToCSVFile( const string& filepath, const string& openmod } // If writing in "w" mode, write the header first. - if( openmode == "w" ) + if(openmode == "w") { string headerText = ""; for( vector<string>::const_iterator it = columns.begin(); it != columns.end(); it++ ) headerText += ( *it + delimiter_ ); headerText += eol; - fprintf( fp, "%s", headerText.c_str() ); + fprintf(fp, "%s", headerText.c_str()); } string text = ""; @@ -111,8 +111,8 @@ void StreamerBase::writeToCSVFile( const string& filepath, const string& openmod // At the end of each row, we remove the delimiter_ and append newline_. *(text.end()-1) = eol; } - fprintf( fp, "%s", text.c_str() ); - fclose( fp ); + fprintf(fp, "%s", text.c_str() ); + fclose(fp); } /* write data to a numpy file */ diff --git a/moose-core/builtins/Table.cpp b/moose-core/builtins/Table.cpp index 4f426b148e09428928a75bb2569bf04c0c2263cd..be3702b13cb3cc46b569af8fc4b319d32dbd6268 100644 --- a/moose-core/builtins/Table.cpp +++ b/moose-core/builtins/Table.cpp @@ -211,24 +211,25 @@ const Cinfo* Table::initCinfo() static const Cinfo* tableCinfo = Table::initCinfo(); Table::Table() : - threshold_( 0.0 ) , - lastTime_( 0.0 ) , - input_( 0.0 ), - fired_(false), - useSpikeMode_(false), - dt_( 0.0 ) + threshold_( 0.0 ) , + lastTime_( 0.0 ) , + input_( 0.0 ), + fired_(false), + useSpikeMode_(false), + dt_( 0.0 ) { // Initialize the directory to which each table should stream. rootdir_ = "_tables"; - useStreamer_ = false; + useFileStreamer_ = false; format_ = "csv"; outfileIsSet_ = false; + lastN_ = 0; } Table::~Table( ) { // Make sure to write to rest of the entries to file before closing down. - if( useStreamer_ ) + if( useFileStreamer_ ) { mergeWithTime( data_ ); StreamerBase::writeToOutFile( outfile_, format_, "a", data_, columns_ ); @@ -267,7 +268,7 @@ void Table::process( const Eref& e, ProcPtr p ) * vector. * Write at every 5 seconds or whenever size of vector is more than 10k. */ - if( useStreamer_ ) + if( useFileStreamer_ ) { if( fmod(lastTime_, 5.0) == 0.0 or getVecSize() >= 10000 ) { @@ -300,7 +301,7 @@ void Table::reinit( const Eref& e, ProcPtr p ) fired_ = false; /** Create the default filepath for this table. */ - if( useStreamer_ ) + if( useFileStreamer_ ) { // The first column is variable time. columns_.push_back( "time" ); @@ -331,7 +332,7 @@ void Table::reinit( const Eref& e, ProcPtr p ) tvec_.push_back(lastTime_); - if( useStreamer_ ) + if( useFileStreamer_ ) { mergeWithTime( data_ ); StreamerBase::writeToOutFile( outfile_, format_, "w", data_, columns_); @@ -412,12 +413,12 @@ void Table::setColumnName( const string colname ) /* Enable/disable streamer support. */ void Table::setUseStreamer( bool useStreamer ) { - useStreamer_ = useStreamer; + useFileStreamer_ = useStreamer; } bool Table::getUseStreamer( void ) const { - return useStreamer_; + return useFileStreamer_; } /* Enable/disable spike mode. */ @@ -477,25 +478,60 @@ void Table::mergeWithTime( vector<double>& data ) /** * @Synopsis. Convert table data to JOSN. * - * @Param t. Upto this time. - * @Param withTime. Zip with time. - * * @Returns string. */ /* ----------------------------------------------------------------------------*/ -string Table::toJSON( bool withTime) +string Table::toJSON(bool withTime, bool clear) { - auto v = vec(); stringstream ss; - for (size_t i = 0; i < v.size(); i++) + auto v = vec(); + if( clear ) + lastN_ = 0; + + for (size_t i = lastN_; i < v.size(); i++) { if(withTime) ss << '[' << tvec_[i] << ',' << v[i] << "],"; else ss << v[i] << ','; } + string res = ss.str(); - if( ',' == res.back()) + if(',' == res.back()) res.pop_back(); + + if( clear ) + clearAllVecs(); + else + lastN_ += v.size(); + return res; } + +/* --------------------------------------------------------------------------*/ +/** + * @Synopsis Collect data in given vector. Its similar to toJSON function. + * + * @Param data + * @Param withTime + * @Param clear + */ +/* ----------------------------------------------------------------------------*/ +void Table::collectData(vector<double>& data, bool withTime, bool clear) +{ + auto v = vec(); + if( clear ) + lastN_ = 0; + + for (size_t i = lastN_; i < v.size(); i++) + { + if(withTime) + data.push_back(tvec_[i]); + data.push_back(v[i]); + } + + if( clear ) + clearAllVecs(); + else + lastN_ = v.size(); +} diff --git a/moose-core/builtins/Table.h b/moose-core/builtins/Table.h index b3a0293bf7796fec8e568063f1e6e2f1e59a09c2..2d59f67dfda3e88cadc40d3b84869f16986f4f09 100644 --- a/moose-core/builtins/Table.h +++ b/moose-core/builtins/Table.h @@ -51,7 +51,10 @@ public: // merge time value among values. e.g. t1, v1, t2, v2, etc. void mergeWithTime( vector<double>& data ); - string toJSON(bool withTime=true); + string toJSON(bool withTime=true, bool clear = false); + + void collectData(vector<double>& data, bool withTime=true, bool clear = false); + void clearAllVecs(); @@ -83,8 +86,11 @@ private: vector<double> tvec_; /* time data */ vector<string> columns_; /* Store the name of tables */ - string tablePath_; + // Upto which indices we have read the data. This variable is used when + // SocketStreamer is used. + size_t lastN_ = 0; + string tablePath_; /** * @brief Column name of this table. Use it when writing data to a datafile. @@ -96,7 +102,7 @@ private: * of outfile_ is table path starting from `pwd`/_tables_ . On table, set * streamToFile to true. */ - bool useStreamer_; + bool useFileStreamer_; /** * @brief Table directory into which dump the stream data. diff --git a/moose-core/kinetics/ReadKkit.cpp b/moose-core/kinetics/ReadKkit.cpp index 01254b62292d6a3cc1f2bdf92638e4c04dcadd3c..a33986ac76052fb13893d53a9a6a9f1ac0b8ab63 100644 --- a/moose-core/kinetics/ReadKkit.cpp +++ b/moose-core/kinetics/ReadKkit.cpp @@ -131,9 +131,10 @@ Id makeStandardElements( Id pa, const string& modelname ) mgr = shell->doCreate( "Neutral", pa, modelname, 1, MooseGlobal ); Id kinetics( modelPath + "/kinetics" ); if ( kinetics == Id() ) { - kinetics = - shell->doCreate( "CubeMesh", mgr, "kinetics", 1, MooseGlobal ); + kinetics = shell->doCreate( "CubeMesh", mgr, "kinetics", 1, MooseGlobal ); SetGet2< double, unsigned int >::set( kinetics, "buildDefaultMesh", 1e-15, 1 ); + Id cInfo = shell->doCreate( "Annotator", kinetics, "info", 1 ); + assert( cInfo != Id() ); } assert( kinetics != Id() ); diff --git a/moose-core/pymoose/moosemodule.cpp b/moose-core/pymoose/moosemodule.cpp index 0a71f46667693213040b1a899ecde7db60b771ea..affdc78f65e3c2006ee607e00a13f9fa1d6168a4 100644 --- a/moose-core/pymoose/moosemodule.cpp +++ b/moose-core/pymoose/moosemodule.cpp @@ -89,9 +89,6 @@ extern void speedTestMultiNodeIntFireNetwork( extern void mooseBenchmarks( unsigned int option ); -// global variable. -bool tcpSocketStreamerEnabled_ = false; - /*----------------------------------------------------------------------------- * Random number generator for this module. * @@ -113,7 +110,6 @@ double pymoose_mtrand_( void ) bool setupSocketStreamer(const string addr ) { LOG(moose::debug, "Setting streamer with addr " << addr ); - // Find all tables. vector< ObjId > tables; wildcardFind( "/##[TYPE=Table2]", tables ); @@ -130,8 +126,9 @@ bool setupSocketStreamer(const string addr ) Id st = SHELLPTR->doCreate("SocketStreamer", stBase, "streamer", 1); Field<string>::set(st, "address", addr); + LOG(moose::debug, "Found " << tables.size() << " tables."); for( auto &t : tables ) - SetGet1<Id>::set(st, "addTable", t.id ); + SetGet1<ObjId>::set(st, "addTable", t ); return true; } @@ -1773,11 +1770,10 @@ PyObject * moose_reinit(PyObject * dummy, PyObject * args) string envSocketServer = moose::getEnv( "MOOSE_STREAMER_ADDRESS" ); if(! envSocketServer.empty()) { - LOG( moose::debug, "Environment variable set of socket" << envSocketServer ); + LOG( moose::debug, "Environment variable MOOSE_STREAMER_ADDRESS: " << envSocketServer ); if( envSocketServer.size() > 0 ) setupSocketStreamer(envSocketServer); } - SHELLPTR->doReinit(); Py_RETURN_NONE; } @@ -1909,7 +1905,7 @@ PyObject * moose_saveModel(PyObject * dummy, PyObject * args) SHELLPTR->doSaveModel(model, filename); Py_RETURN_NONE; } -#endif +#endif PyObject * moose_setCwe(PyObject * dummy, PyObject * args) { @@ -2971,17 +2967,17 @@ PyObject * moose_element(PyObject* dummy, PyObject * args) unsigned nid = 0, did = 0, fidx = 0; Id id; unsigned int numData = 0; - if (PyArg_ParseTuple(args, "s", &path)) + + // Parse into str or bytes-like object. Using 's' parses into const char* + // which is portable with bytes often returned when working with python3. + if (PyArg_ParseTuple(args, "s*", &path)) { oid = ObjId(path); - // cout << "Original Path " << path << ", Element Path: " << oid.path() << endl; if ( oid.bad() ) { - PyErr_SetString(PyExc_ValueError, ( std::string("moose_element: '") - + std::string(path) - + std::string("' does not exist!") - ).c_str() - ); + PyErr_SetString(PyExc_ValueError + , (std::string("moose_element: '") + std::string(path) + std::string("' does not exist!")).c_str() + ); return NULL; } PyObject * new_obj = oid_to_element(oid); @@ -3272,4 +3268,4 @@ PyMODINIT_FUNC MODINIT(_moose) #ifdef PY3K return moose_module; #endif -} +} diff --git a/moose-core/pymoose/vec.cpp b/moose-core/pymoose/vec.cpp index e5e2ddb3ec987d5870474dfe1593239daa5fe69f..fc57831ce5195d4bab16b934e29dc251bed2f6de 100644 --- a/moose-core/pymoose/vec.cpp +++ b/moose-core/pymoose/vec.cpp @@ -48,8 +48,6 @@ #include <Python.h> #include <structmember.h> // This defines the type id macros like T_STRING -// #include "numpy/arrayobject.h" - #include "../utility/simple_logger.hpp" #include <iostream> @@ -1259,20 +1257,12 @@ int moose_Id_setattro(_Id * self, PyObject * attr, PyObject *value) default: break; } + // MOOSE Field::set returns 1 for success 0 for // failure. Python treats return value 0 from setters as // success, anything else failure. if (ret && (PyErr_Occurred() == NULL)) - { return 0; - } else - { return -1; - } - } - - -// -// vec.cpp ends here diff --git a/moose-core/python/moose/SBML/readSBML.py b/moose-core/python/moose/SBML/readSBML.py index 356d2400314d2b4623594f8057943bdfc93031ef..093f20854e6c34e1c0521844e789e841999d9331 100644 --- a/moose-core/python/moose/SBML/readSBML.py +++ b/moose-core/python/moose/SBML/readSBML.py @@ -13,9 +13,13 @@ ** copyright (C) 2003-2017 Upinder S. Bhalla. and NCBS Created : Thu May 13 10:19:00 2016(+0530) Version -Last-Updated: Tue Dec 3 17:30:00 2018(+0530) +Last-Updated: Sat Jan 19 10:30:00 2019(+0530) By:HarshaRani **********************************************************************/ +2019: +Jan 19: - validator flag is set 'on' from True + - groupname if missing in the sbml file then groupid is taken, + if both are missing then its not a valide sbml file 2018 Dec 3: - reading motor and diffconstant from pool Nov 30: - groups and subgroups are read from xml to moose @@ -82,7 +86,7 @@ try: except ImportError: pass -def mooseReadSBML(filepath, loadpath, solver="ee",validate="True"): +def mooseReadSBML(filepath, loadpath, solver="ee",validate="on"): """Load SBML model """ global foundLibSBML_ @@ -102,13 +106,15 @@ def mooseReadSBML(filepath, loadpath, solver="ee",validate="True"): with open(filepath, "r") as filep: loadpath = loadpath[loadpath.find('/')+1:] loaderror = None + errorFlag = "" filep = open(filepath, "r") document = libsbml.readSBML(filepath) tobecontinue = False - if validate == "True": - tobecontinue = validateModel(document) + if validate == "on": + tobecontinue,errorFlag = validateModel(document) else: tobecontinue = True + if tobecontinue: level = document.getLevel() version = document.getVersion() @@ -126,6 +132,7 @@ def mooseReadSBML(filepath, loadpath, solver="ee",validate="True"): loadpath ='/'+loadpath baseId = moose.Neutral(loadpath) basePath = baseId + # All the model will be created under model as # a thumbrule basePath = moose.Neutral(baseId.path) @@ -150,7 +157,7 @@ def mooseReadSBML(filepath, loadpath, solver="ee",validate="True"): errorFlag,msgCmpt = createCompartment( basePath, model, comptSbmlidMooseIdMap) - groupInfo = checkGroup(basePath,model) + groupInfo = checkGroup(basePath,model,comptSbmlidMooseIdMap) funcDef = checkFuncDef(model) if errorFlag: specInfoMap = {} @@ -207,8 +214,11 @@ def mooseReadSBML(filepath, loadpath, solver="ee",validate="True"): loaderror = loaderror return moose.element(loadpath), loaderror else: - print("Validation failed while reading the model.") - return moose.element('/'), "This document is not valid SBML" + print("Validation failed while reading the model."+"\n"+errorFlag) + if errorFlag != "": + return moose.element('/'),errorFlag + else: + return moose.element('/'), "This document is not valid SBML" def checkFuncDef(model): funcDef = {} @@ -229,7 +239,7 @@ def checkFuncDef(model): funcDef[f.getName()] = {'bvar':bvar, "MathML": fmath.getRightChild()} return funcDef -def checkGroup(basePath,model): +def checkGroup(basePath,model,comptSbmlidMooseIdMap): groupInfo = {} modelAnnotaInfo = {} if model.getPlugin("groups") != None: @@ -237,27 +247,33 @@ def checkGroup(basePath,model): modelgn = mplugin.getNumGroups() for gindex in range(0, mplugin.getNumGroups()): p = mplugin.getGroup(gindex) + grpName = "" groupAnnoInfo = {} groupAnnoInfo = getObjAnnotation(p, modelAnnotaInfo) + if groupAnnoInfo != {}: - if moose.exists(basePath.path+'/'+groupAnnoInfo["Compartment"]): + if moose.exists(basePath.path+'/'+comptSbmlidMooseIdMap[groupAnnoInfo["Compartment"]]["MooseId"].name): + groupName = p.getName() + if groupName == " ": + groupName = p.getId() if "Group" in groupAnnoInfo: - if moose.exists(basePath.path+'/'+groupAnnoInfo["Compartment"]+'/'+groupAnnoInfo["Group"]): - if moose.exists(basePath.path+'/'+groupAnnoInfo["Compartment"]+'/'+groupAnnoInfo["Group"]+'/'+p.getName()): - moosegrp = moose.element(basePath.path+'/'+groupAnnoInfo["Compartment"]+'/'+groupAnnoInfo["Group"]+'/'+p.getName()) + if moose.exists(basePath.path+'/'+comptSbmlidMooseIdMap[groupAnnoInfo["Compartment"]]["MooseId"].name+'/'+groupAnnoInfo["Group"]): + if moose.exists(basePath.path+'/'+comptSbmlidMooseIdMap[groupAnnoInfo["Compartment"]]["MooseId"].name+'/'+groupAnnoInfo["Group"]+'/'+groupName): + moosegrp = moose.element(basePath.path+'/'+comptSbmlidMooseIdMap[groupAnnoInfo["Compartment"]]["MooseId"].name+'/'+groupAnnoInfo["Group"]+'/'+groupName) else: - moosegrp = moose.Neutral(basePath.path+'/'+groupAnnoInfo["Compartment"]+'/'+groupAnnoInfo["Group"]+'/'+p.getName()) + moosegrp = moose.Neutral(basePath.path+'/'+comptSbmlidMooseIdMap[groupAnnoInfo["Compartment"]]["MooseId"].name+'/'+groupAnnoInfo["Group"]+'/'+groupName) else: - moose.Neutral(basePath.path+'/'+groupAnnoInfo["Compartment"]+'/'+groupAnnoInfo["Group"]) - if moose.exists(basePath.path+'/'+groupAnnoInfo["Compartment"]+'/'+groupAnnoInfo["Group"]+'/'+p.getName()): - moosegrp = moose.element(basePath.path+'/'+groupAnnoInfo["Compartment"]+'/'+groupAnnoInfo["Group"]+'/'+p.getName()) + moose.Neutral(basePath.path+'/'+comptSbmlidMooseIdMap[groupAnnoInfo["Compartment"]]["MooseId"].name+'/'+groupAnnoInfo["Group"]) + if moose.exists(basePath.path+'/'+comptSbmlidMooseIdMap[groupAnnoInfo["Compartment"]]["MooseId"].name+'/'+groupAnnoInfo["Group"]+'/'+groupName): + moosegrp = moose.element(basePath.path+'/'+comptSbmlidMooseIdMap[groupAnnoInfo["Compartment"]]["MooseId"].name+'/'+groupAnnoInfo["Group"]+'/'+groupName) else: - moosegrp = moose.Neutral(basePath.path+'/'+groupAnnoInfo["Compartment"]+'/'+groupAnnoInfo["Group"]+'/'+p.getName()) + moosegrp = moose.Neutral(basePath.path+'/'+comptSbmlidMooseIdMap[groupAnnoInfo["Compartment"]]["MooseId"].name+'/'+groupAnnoInfo["Group"]+'/'+groupName) else: - if not moose.exists(basePath.path+'/'+groupAnnoInfo["Compartment"]+'/'+p.getName()): - moosegrp = moose.Neutral(basePath.path+'/'+groupAnnoInfo["Compartment"]+'/'+p.getName()) + if not moose.exists(basePath.path+'/'+comptSbmlidMooseIdMap[groupAnnoInfo["Compartment"]]["MooseId"].name+'/'+groupName): + moosegrp = moose.Neutral(basePath.path+'/'+comptSbmlidMooseIdMap[groupAnnoInfo["Compartment"]]["MooseId"].name+'/'+groupName) else: - moosegrp = moose.element(basePath.path+'/'+groupAnnoInfo["Compartment"]+'/'+p.getName()) + moosegrp = moose.element(basePath.path+'/'+comptSbmlidMooseIdMap[groupAnnoInfo["Compartment"]]["MooseId"].name+'/'+groupName) + moosegrpinfo = moose.Annotator(moosegrp.path+'/info') moosegrpinfo.color = groupAnnoInfo["bgColor"] else: @@ -1206,7 +1222,7 @@ def createSpecies(basePath, model, comptSbmlidMooseIdMap, poolInfo = moose.Annotator(poolId.path + '/info') else: poolInfo = moose.element(poolId.path + '/info') - + for k, v in list(specAnnoInfo.items()): if k == 'xCord': poolInfo.x = float(v) @@ -1214,12 +1230,13 @@ def createSpecies(basePath, model, comptSbmlidMooseIdMap, poolInfo.y = float(v) elif k == 'bgColor': poolInfo.color = v - elif k == 'Color': + elif k == 'textColor': poolInfo.textColor = v elif k == 'diffConstant': poolId.diffConst = float(v) elif k == 'motorConstant': - poolId.motorConst = float(v) + poolId.motorConst = float(v) + specInfoMap[sId] = { "Mpath": poolId, "const": constant, diff --git a/moose-core/python/moose/SBML/validation.py b/moose-core/python/moose/SBML/validation.py index 48facdf07440d16e2dcb37fbdad470506c4d9c3f..1291c0c5c9f7a6851973cf967009159f23af3b1a 100644 --- a/moose-core/python/moose/SBML/validation.py +++ b/moose-core/python/moose/SBML/validation.py @@ -18,7 +18,11 @@ Last-Updated: Fri Jul 28 15:50:00 2017(+0530) By: **********************************************************************/ +**********************************************************************/ +2019 +Jan 19: - returned errorMsg ''' + foundLibSBML_ = False try: from libsbml import * @@ -29,13 +33,15 @@ except Exception as e: def validateModel(sbmlDoc): if sbmlDoc.getNumErrors() > 0: tobecontinued = False + validError = "" for i in range(0,sbmlDoc.getNumErrors()): - print (sbmlDoc.getError(i).getMessage()) - return False + validError = validError+sbmlDoc.getError(i).getMessage() + #print (validError) + return False, validError if (not sbmlDoc): print("validateModel: given a null SBML Document") - return False + return False, "validateModel: given a null SBML Document" consistencyMessages = "" validationMessages = "" @@ -86,7 +92,7 @@ def validateModel(sbmlDoc): validationMessages = oss if (noProblems): - return True + return True,"" else: if consistencyMessages is None: consistencyMessages = "" @@ -107,7 +113,7 @@ def validateModel(sbmlDoc): if validationMessages: print(validationMessages) - return False + return False,validationMessages # return ( numConsistencyErrors == 0 and numValidationErrors == 0, # consistencyMessages) diff --git a/moose-core/python/moose/SBML/writeSBML.py b/moose-core/python/moose/SBML/writeSBML.py index d8325ee2a978cb49af6992f48ded5daa347da607..0f021c670e95150e7f9eb3401c745f50be7651bc 100644 --- a/moose-core/python/moose/SBML/writeSBML.py +++ b/moose-core/python/moose/SBML/writeSBML.py @@ -13,10 +13,12 @@ ** copyright (C) 2003-2017 Upinder S. Bhalla. and NCBS Created : Friday May 27 12:19:00 2016(+0530) Version -Last-Updated: Tue 3 Dec 15:15:10 2018(+0530) +Last-Updated: Tue 29 Jan 15:15:10 2019(+0530) By: HarshaRani **********************************************************************/ /**************************** +2019 +Jan 29: getColor are taken from chemConnectUtil, group's width and height are written 2018 Dec 07: using fixXreac's restoreXreacs function to remove xfer Dec 03: add diff and motor constants to pool @@ -47,9 +49,10 @@ import re import os import moose from moose.SBML.validation import validateModel -from moose.chemUtil.chemConnectUtil import * +from moose.chemUtil.chemConnectUtil import xyPosition,mooseIsInstance,findCompartment,getColor,setupItem from moose.chemUtil.graphUtils import * from moose.fixXreacs import restoreXreacs +import numpy as np foundLibSBML_ = False try: @@ -98,6 +101,7 @@ def mooseWriteSBML(modelpath, filename, sceneitems={}): if moose.exists(p.path+'/info'): xcord.append(moose.element(p.path+'/info').x) ycord.append(moose.element(p.path+'/info').y) + getColor(moose.element(p.path+'/info').path) recalculatecoordinates(modelpath,mObj,xcord,ycord) positionInfoexist = False @@ -158,8 +162,8 @@ def mooseWriteSBML(modelpath, filename, sceneitems={}): mplugin = cremodel_.getPlugin("groups") group = mplugin.createGroup() name = str(idBeginWith(moose.element(key).name)) - moosegrpId = name +"_" + str(moose.element(key).getId().value) + "_" + str(moose.element(key).getDataIndex()) - group.setId(moosegrpId) + moosegrpId = name +"_" + str(moose.element(key).getId().value) + "_" + str(moose.element(key).getDataIndex())+"_" + group.setId(moosegrpId) group.setName(name) group.setKind("collection") @@ -167,13 +171,32 @@ def mooseWriteSBML(modelpath, filename, sceneitems={}): ginfo = moose.element(key.path+'/info') else: ginfo = moose.Annotator(key.path+'/info') + groupCompartment = findCompartment(key) + grpAnno = "<moose:GroupAnnotation>" - grpAnno = grpAnno + "<moose:Compartment>" + groupCompartment.name + "</moose:Compartment>\n" + grpAnno = grpAnno + "<moose:Compartment>" + groupCompartment.name +"_"+ str(moose.element(groupCompartment).getId().value) +"_"+ str(moose.element(groupCompartment).getDataIndex())+"_"+"</moose:Compartment>\n" + #grpAnno = grpAnno + "<moose:Compartment>" + groupCompartment.name + "_" + str(moose.element(groupCompartment).getId().value) + "_" + str(moose.element(groupCompartment).getDataIndex()) + "_"+ "</moose:Compartment>\n" if moose.element(key.parent).className == "Neutral": + grpAnno = grpAnno + "<moose:Group>" + key.parent.name + "</moose:Group>\n" - if ginfo.color: - grpAnno = grpAnno + "<moose:bgColor>" + ginfo.color + "</moose:bgColor>\n" + grpparent = key.parent.name + "_" + str(moose.element(key.parent).getId().value) + "_" + str(moose.element(key.parent).getDataIndex()) + "_" + grpAnno = grpAnno + "<moose:Parent>" + grpparent + "</moose:Parent>\n" + else: + grpparent = groupCompartment.name + "_" + str(moose.element(groupCompartment).getId().value) + "_" + str(moose.element(groupCompartment).getDataIndex()) + "_" + grpAnno = grpAnno + "<moose:Parent>" + grpparent + "</moose:Parent>\n" + + if moose.exists(key.path+'/info'): + ginfo = moose.element(key.path+'/info') + if ginfo.height and ginfo.width: + grpAnno = grpAnno + "<moose:x>" + str(ginfo.x) + "</moose:x>\n" + grpAnno = grpAnno + "<moose:y>" + str(ginfo.y) + "</moose:y>\n" + grpAnno = grpAnno + "<moose:width>" + str(ginfo.width) + "</moose:width>\n" + grpAnno = grpAnno + "<moose:height>" + str(ginfo.height) + "</moose:height>\n" + if ginfo.color: + grpAnno = grpAnno + "<moose:bgColor>" + ginfo.color + "</moose:bgColor>\n" + if ginfo.notes: + grpAnno = grpAnno + "<moose:Notes>" + ginfo.notes + "</moose:Notes>\n" grpAnno = grpAnno + "</moose:GroupAnnotation>" group.setAnnotation(grpAnno) @@ -1048,8 +1071,12 @@ def writeCompt(modelpath, cremodel_): "<moose:surround>" + \ str(comptID_sbml[compt.surround])+ "</moose:surround>\n" + \ "<moose:isMembraneBound>" + \ - str(compt.isMembraneBound)+ "</moose:isMembraneBound>\n" + \ - "</moose:CompartmentAnnotation>" + str(compt.isMembraneBound)+ "</moose:isMembraneBound>\n" + if moose.exists(compt.path+'/info'): + if moose.element(compt.path+'/info').notes != "": + comptAnno = comptAnno + "<moose:Notes>" \ + + moose.element(compt.path+'/info').notes + "</moose:Notes>" + comptAnno = comptAnno+ "</moose:CompartmentAnnotation>" elif isinstance (compt,moose.CylMesh) : size = (compt.volume/compt.numDiffCompts)*pow(10,3) comptAnno = "<moose:CompartmentAnnotation><moose:Mesh>" + \ @@ -1059,14 +1086,22 @@ def writeCompt(modelpath, cremodel_): "<moose:diffLength>" + \ str(compt.diffLength)+ "</moose:diffLength>\n" + \ "<moose:isMembraneBound>" + \ - str(compt.isMembraneBound)+ "</moose:isMembraneBound>\n" + \ - "</moose:CompartmentAnnotation>" + str(compt.isMembraneBound)+ "</moose:isMembraneBound>\n" + if moose.exists(compt.path+'/info'): + if moose.element(compt.path+'/info').notes != "": + comptAnno = comptAnno + "<moose:Notes>" \ + + moose.element(compt.path+'/info').notes + "</moose:Notes>" + comptAnno = comptAnno+ "</moose:CompartmentAnnotation>" else: comptAnno = "<moose:CompartmentAnnotation><moose:Mesh>" + \ str(compt.className) + "</moose:Mesh>\n" + \ "<moose:isMembraneBound>" + \ - str(compt.isMembraneBound)+ "</moose:isMembraneBound>\n" + \ - "</moose:CompartmentAnnotation>" + str(compt.isMembraneBound)+ "</moose:isMembraneBound>\n" + if moose.exists(compt.path+'/info'): + if moose.element(compt.path+'/info').notes != "": + comptAnno = comptAnno + "<moose:Notes>" \ + + moose.element(compt.path+'/info').notes + "</moose:Notes>" + comptAnno = comptAnno+ "</moose:CompartmentAnnotation>" if createCompt: c1 = cremodel_.createCompartment() c1.setId(csetId) diff --git a/moose-core/python/moose/chemUtil/chemConnectUtil.py b/moose-core/python/moose/chemUtil/chemConnectUtil.py index 9c92e613786c8448f0075ac67bf46cc92e14c05e..f15708be7b7e084a4ad8dfdd3ed3675d943b65c8 100644 --- a/moose-core/python/moose/chemUtil/chemConnectUtil.py +++ b/moose-core/python/moose/chemUtil/chemConnectUtil.py @@ -9,10 +9,105 @@ __version__ = "1.0.0" __maintainer__ = "Harsha Rani" __email__ = "hrani@ncbs.res.in" __status__ = "Development" -__updated__ = "Nov 8 2018" +__updated__ = "Mar 11 2019" import moose import numpy as np +import pickle +import matplotlib +import os +import random + + +direct = os.path.dirname(__file__) +colormap_file = open(os.path.join(direct, "rainbow2.pkl"), "r") +colorMap = pickle.loads(colormap_file.read().encode('utf8')) +colormap_file.close() +ignoreColor= ["mistyrose","antiquewhite","aliceblue","azure","bisque","black","blanchedalmond","blue","cornsilk","darkolivegreen","darkslategray","dimgray","floralwhite","gainsboro","ghostwhite","honeydew","ivory","lavender","lavenderblush","lemonchiffon","lightcyan","lightgoldenrodyellow","lightgray","lightyellow","linen","mediumblue","mintcream","navy","oldlace","papayawhip","saddlebrown","seashell","snow","wheat","white","whitesmoke","aquamarine","lightsalmon","moccasin","limegreen","snow","sienna","beige","dimgrey","lightsage"] +matplotcolor = {} +for name,hexno in matplotlib.colors.cnames.items(): + matplotcolor[name]=hexno + +def getRandColor(): + k = random.choice(matplotcolor.keys()) + if k in ignoreColor: + return getRandColor() + else: + #return matplotcolor[k] + return str(matplotlib.colors.cnames[k]) + +def getColor(iteminfo): + """ Getting a textcolor and background color for the given mooseObject \ + If textcolor is empty replaced with green \ + background color is empty replaced with blue + if textcolor and background is same as it happend in kkit files \ + replacing textcolor with random color\ + The colors are not valid there are siliently replaced with some values \ + but while model building can raise an exception + """ + textcolor = moose.element(iteminfo).textColor + bgcolor = moose.element(iteminfo).color + if(textcolor == ''): textcolor = 'green' + + if(bgcolor == ''): bgcolor = 'orange' + + if(textcolor == bgcolor): + textcolor = getRandColor() + + textcolor = colorCheck(textcolor) + bgcolor = colorCheck(bgcolor) + # if moose.exists(iteminfo): + # moose.element(iteminfo).textColor = textcolor + # moose.element(iteminfo).color = bgcolor + return(textcolor,bgcolor) + +def colorCheck(fc_bgcolor): + """ textColor or background can be anything like string or tuple or list \ + if string its taken as colorname further down in validColorcheck checked for valid color, \ + but for tuple and list its taken as r,g,b value. + """ + if isinstance(fc_bgcolor,str): + if fc_bgcolor.startswith("#"): + fc_bgcolor = fc_bgcolor + elif fc_bgcolor.isdigit(): + """ color is int a map from int to r,g,b triplets from pickled color map file """ + tc = (int(fc_bgcolor))*2 + if tc < len(colorMap): + pickledColor = colorMap[tc] + else: + pickledColor = (255, 0, 0) + fc_bgcolor = '#%02x%02x%02x' % (pickledColor) + + elif fc_bgcolor.isalpha() or fc_bgcolor.isalnum(): + fc_bgcolor = validColorcheck(fc_bgcolor) + + else: + for r in ['[',']','(',')']: + fc_bgcolor = fc_bgcolor.replace(r,"") + fc_bgcolor = fc_bgcolor.split(",") + c = 0 + hexlist ="#" + for n in fc_bgcolor: + if c < 3: + hexlist = hexlist+str("%02x" % int(n)) + c = c+1; + fc_bgcolor = hexlist + return(fc_bgcolor) + +def validColorcheck(color): + ''' + Both in Qt4.7 and 4.8 if not a valid color it makes it as back but in 4.7 there will be a warning mssg which is taken here + checking if textcolor or backgroundcolor is valid color, if 'No' making white color as default + where I have not taken care for checking what will be backgroundcolor for textcolor or textcolor for backgroundcolor + ''' + #if QColor(color).isValid(): + if matplotlib.colors.is_color_like(color): + if color == "blue": + color = "orange" + color = matplotlib.colors.cnames[color.lower()] + return color + else: + return(matplotlib.colors.cnames["orange"]) def xyPosition(objInfo,xory): try: @@ -89,7 +184,7 @@ def setupMeshObj(modelRoot): ymin = min(ycord) ymax = max(ycord) return meshEntry,xmin,xmax,ymin,ymax,positionInfoExist,listOfitems - + def getxyCord(xcord,ycord,list1,listOfitems): for item in list1: # if isinstance(item,Function): diff --git a/moose-core/python/moose/chemUtil/rainbow2.pkl b/moose-core/python/moose/chemUtil/rainbow2.pkl new file mode 100644 index 0000000000000000000000000000000000000000..8097c29c7e173bd42aaec157acaf806579f61af2 --- /dev/null +++ b/moose-core/python/moose/chemUtil/rainbow2.pkl @@ -0,0 +1,514 @@ +((I248 +I0 +I255 +tp0 +(I240 +I0 +I255 +tp1 +(I232 +I0 +I255 +tp2 +(I224 +I0 +I255 +tp3 +(I216 +I0 +I255 +tp4 +(I208 +I0 +I255 +tp5 +(I200 +I0 +I255 +tp6 +(I192 +I0 +I255 +tp7 +(I184 +I0 +I255 +tp8 +(I176 +I0 +I255 +tp9 +(I168 +I0 +I255 +tp10 +(I160 +I0 +I255 +tp11 +(I152 +I0 +I255 +tp12 +(I144 +I0 +I255 +tp13 +(I136 +I0 +I255 +tp14 +(I128 +I0 +I255 +tp15 +(I120 +I0 +I255 +tp16 +(I112 +I0 +I255 +tp17 +(I104 +I0 +I255 +tp18 +(I96 +I0 +I255 +tp19 +(I88 +I0 +I255 +tp20 +(I80 +I0 +I255 +tp21 +(I72 +I0 +I255 +tp22 +(I64 +I0 +I255 +tp23 +(I56 +I0 +I255 +tp24 +(I48 +I0 +I255 +tp25 +(I40 +I0 +I255 +tp26 +(I32 +I0 +I255 +tp27 +(I24 +I0 +I255 +tp28 +(I16 +I0 +I255 +tp29 +(I8 +I0 +I255 +tp30 +(I0 +I0 +I255 +tp31 +(I0 +I8 +I248 +tp32 +(I0 +I16 +I240 +tp33 +(I0 +I24 +I232 +tp34 +(I0 +I32 +I224 +tp35 +(I0 +I40 +I216 +tp36 +(I0 +I48 +I208 +tp37 +(I0 +I56 +I200 +tp38 +(I0 +I64 +I192 +tp39 +(I0 +I72 +I184 +tp40 +(I0 +I80 +I176 +tp41 +(I0 +I88 +I168 +tp42 +(I0 +I96 +I160 +tp43 +(I0 +I104 +I152 +tp44 +(I0 +I112 +I144 +tp45 +(I0 +I120 +I136 +tp46 +(I0 +I128 +I128 +tp47 +(I0 +I136 +I120 +tp48 +(I0 +I144 +I112 +tp49 +(I0 +I152 +I104 +tp50 +(I0 +I160 +I96 +tp51 +(I0 +I168 +I88 +tp52 +(I0 +I176 +I80 +tp53 +(I0 +I184 +I72 +tp54 +(I0 +I192 +I64 +tp55 +(I0 +I200 +I56 +tp56 +(I0 +I208 +I48 +tp57 +(I0 +I216 +I40 +tp58 +(I0 +I224 +I32 +tp59 +(I0 +I232 +I24 +tp60 +(I0 +I240 +I16 +tp61 +(I0 +I248 +I8 +tp62 +(I0 +I255 +I0 +tp63 +(I8 +I255 +I0 +tp64 +(I16 +I255 +I0 +tp65 +(I24 +I255 +I0 +tp66 +(I32 +I255 +I0 +tp67 +(I40 +I255 +I0 +tp68 +(I48 +I255 +I0 +tp69 +(I56 +I255 +I0 +tp70 +(I64 +I255 +I0 +tp71 +(I72 +I255 +I0 +tp72 +(I80 +I255 +I0 +tp73 +(I88 +I255 +I0 +tp74 +(I96 +I255 +I0 +tp75 +(I104 +I255 +I0 +tp76 +(I112 +I255 +I0 +tp77 +(I120 +I255 +I0 +tp78 +(I128 +I255 +I0 +tp79 +(I136 +I255 +I0 +tp80 +(I144 +I255 +I0 +tp81 +(I152 +I255 +I0 +tp82 +(I160 +I255 +I0 +tp83 +(I168 +I255 +I0 +tp84 +(I176 +I255 +I0 +tp85 +(I184 +I255 +I0 +tp86 +(I192 +I255 +I0 +tp87 +(I200 +I255 +I0 +tp88 +(I208 +I255 +I0 +tp89 +(I216 +I255 +I0 +tp90 +(I224 +I255 +I0 +tp91 +(I232 +I255 +I0 +tp92 +(I240 +I255 +I0 +tp93 +(I248 +I255 +I0 +tp94 +(I255 +I255 +I0 +tp95 +(I255 +I248 +I0 +tp96 +(I255 +I240 +I0 +tp97 +(I255 +I232 +I0 +tp98 +(I255 +I224 +I0 +tp99 +(I255 +I216 +I0 +tp100 +(I255 +I208 +I0 +tp101 +(I255 +I200 +I0 +tp102 +(I255 +I192 +I0 +tp103 +(I255 +I184 +I0 +tp104 +(I255 +I176 +I0 +tp105 +(I255 +I168 +I0 +tp106 +(I255 +I160 +I0 +tp107 +(I255 +I152 +I0 +tp108 +(I255 +I144 +I0 +tp109 +(I255 +I136 +I0 +tp110 +(I255 +I128 +I0 +tp111 +(I255 +I120 +I0 +tp112 +(I255 +I112 +I0 +tp113 +(I255 +I104 +I0 +tp114 +(I255 +I96 +I0 +tp115 +(I255 +I88 +I0 +tp116 +(I255 +I80 +I0 +tp117 +(I255 +I72 +I0 +tp118 +(I255 +I64 +I0 +tp119 +(I255 +I56 +I0 +tp120 +(I255 +I48 +I0 +tp121 +(I255 +I40 +I0 +tp122 +(I255 +I32 +I0 +tp123 +(I255 +I24 +I0 +tp124 +(I255 +I16 +I0 +tp125 +(I255 +I8 +I0 +tp126 +(I255 +I0 +I0 +tp127 +tp128 +. \ No newline at end of file diff --git a/moose-core/python/moose/genesis/writeKkit.py b/moose-core/python/moose/genesis/writeKkit.py index 6227bc0b638f7ec00e61250c5d8169c1a6dcabcf..009f229911812e95e84ddc660050070ec855a699 100644 --- a/moose-core/python/moose/genesis/writeKkit.py +++ b/moose-core/python/moose/genesis/writeKkit.py @@ -271,9 +271,9 @@ def writeConcChan(modelpath,f,sceneitems): else: error = error + "\n x and y co-ordinates are not specified for `" + cChan.name+ "` zero will be assigned \n " if color == "" or color == " ": - color = getRandColor() + color = getRandomColor() if textcolor == "" or textcolor == " ": - textcolor = getRandColor() + textcolor = getRandomColor() f.write("simundump kchan /kinetics/" + trimPath(cChan)+ " " + str(int(1)) + " " + str(cChan.permeability)+ " " + str(int(0)) + " " + str(int(0)) + " " + @@ -344,9 +344,9 @@ def writeEnz( modelpath,f,sceneitems): else: error = error + "\n x and y co-ordinates are not specified for `" + enz.name+ "` zero will be assigned \n " if color == "" or color == " ": - color = getRandColor() + color = getRandomColor() if textcolor == "" or textcolor == " ": - textcolor = getRandColor() + textcolor = getRandomColor() f.write("simundump kenz /kinetics/" + trimPath(enz) + " " + str(int(0))+ " " + str(concInit) + " " + @@ -421,9 +421,9 @@ def writeReac(modelpath,f,sceneitems): y = 0 error = error + "\n x and y co-ordinates are not specified for `" + reac.name+ "` zero will be assigned \n " if color == "" or color == " ": - color = getRandColor() + color = getRandomColor() if textcolor == "" or textcolor == " ": - textcolor = getRandColor() + textcolor = getRandomColor() f.write("simundump kreac /kinetics/" + trimPath(reac) + " " +str(0) +" "+ str(kf) + " " + str(kb) + " \"\" " + str(color) + " " + str(textcolor) + " " + str(int(x)) + " " + str(int(y)) + " "+ str(0)+"\n") return reacList,error @@ -574,9 +574,9 @@ def writeplot( tgraphs,f ): fg = getColorCheck(fg,GENESIS_COLOR_SEQUENCE) tabPath = re.sub("\[[0-9]+\]", "", tabPath) if tabPath.find("conc1") >= 0 or tabPath.find("conc2") >= 0: - first = first + "simundump xplot " + tabPath + " 3 524288 \\\n" + "\"delete_plot.w <s> <d>; edit_plot.D <w>\" " + fg + " 0 0 1\n" + first = first + "simundump xplot " + tabPath + " 3 524288 \\\n" + "\"delete_plot.w <s> <d>; edit_plot.D <w>\" " + str(fg) + " 0 0 1\n" if tabPath.find("conc3") >= 0 or tabPath.find("conc4") >= 0: - second = second + "simundump xplot " + tabPath + " 3 524288 \\\n" + "\"delete_plot.w <s> <d>; edit_plot.D <w>\" " + fg + " 0 0 1\n" + second = second + "simundump xplot " + tabPath + " 3 524288 \\\n" + "\"delete_plot.w <s> <d>; edit_plot.D <w>\" " + str(fg) + " 0 0 1\n" return first,second def writePool(modelpath,f,volIndex,sceneitems): @@ -628,9 +628,9 @@ def writePool(modelpath,f,volIndex,sceneitems): geometryName = volIndex[float(poolsCmpt.volume)] volume = p.volume * NA * 1e-3 if color == "" or color == " ": - color = getRandColor() + color = getRandomColor() if textcolor == "" or textcolor == " ": - textcolor = getRandColor() + textcolor = getRandomColor() f.write("simundump kpool /kinetics/" + trimPath(p) + " 0 " + str(p.diffConst) + " " + str(0) + " " + @@ -673,7 +673,7 @@ def getColorCheck(color,GENESIS_COLOR_SEQUENCE): else: raise Exception("Invalid Color Value!") -def getRandColor(): +def getRandomColor(): ignoreColor= ["mistyrose","antiquewhite","aliceblue","azure","bisque","black","blanchedalmond","blue","cornsilk","darkolivegreen","darkslategray","dimgray","floralwhite","gainsboro","ghostwhite","honeydew","ivory","lavender","lavenderblush","lemonchiffon","lightcyan","lightgoldenrodyellow","lightgray","lightyellow","linen","mediumblue","mintcream","navy","oldlace","papayawhip","saddlebrown","seashell","snow","wheat","white","whitesmoke","aquamarine","lightsalmon","moccasin","limegreen","snow","sienna","beige","dimgrey","lightsage"] matplotcolor = {} for name,hexno in matplotlib.colors.cnames.items(): @@ -681,7 +681,7 @@ def getRandColor(): k = random.choice(list(matplotcolor.keys())) if k in ignoreColor: - return getRandColor() + return getRandomColor() else: return k def writeCompartment(modelpath,compts,f): diff --git a/moose-core/python/moose/helper.py b/moose-core/python/moose/helper.py new file mode 100644 index 0000000000000000000000000000000000000000..79af6868a338709dc03d5a66aae15491ee8a4d1c --- /dev/null +++ b/moose-core/python/moose/helper.py @@ -0,0 +1,83 @@ +"""helper.py: + +Some helper functions which are compatible with both python2 and python3. +""" + +__author__ = "Dilawar Singh" +__copyright__ = "Copyright 2017-, Dilawar Singh" +__version__ = "1.0.0" +__maintainer__ = "Dilawar Singh" +__email__ = "dilawars@ncbs.res.in" +__status__ = "Development" + +import os +import re +import subprocess + +def execute(cmd): + """execute: Execute a given command. + + :param cmd: string, given command. + + Return: + ------ + Return a iterator over output. + """ + popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True) + for stdout_line in iter(popen.stdout.readline, ""): + yield stdout_line + popen.stdout.close() + return_code = popen.wait() + if return_code: + raise subprocess.CalledProcessError(return_code, cmd) + + +def find_files( dirname, ext=None, name_contains=None, text_regex_search=None): + files = [] + for d, sd, fs in os.walk(dirname): + for f in fs: + fpath = os.path.join(d,f) + include = True + if ext is not None: + if f.split('.')[-1] != ext: + include = False + if name_contains: + if name_contains not in os.path.basename(f): + include = False + if text_regex_search: + with open(fpath, 'r' ) as f: + txt = f.read() + if re.search(text_regex_search, txt) is None: + include = False + if include: + files.append(fpath) + return files + +# Matplotlib text for running simulation. It make sures at each figure is saved +# to individual png files. +matplotlibText = """ +print( '>>>> saving all figues') +import matplotlib.pyplot as plt +def multipage(filename, figs=None, dpi=200): + pp = PdfPages(filename) + if figs is None: + figs = [plt.figure(n) for n in plt.get_fignums()] + for fig in figs: + fig.savefig(pp, format='pdf') + pp.close() + +def saveall(prefix='results', figs=None): + if figs is None: + figs = [plt.figure(n) for n in plt.get_fignums()] + for i, fig in enumerate(figs): + outfile = '%s.%d.png' % (prefix, i) + fig.savefig(outfile) + print( '>>>> %s saved.' % outfile ) + plt.close() + +try: + saveall() +except Exception as e: + print( '>>>> Error in saving: %s' % e ) + quit(0) +""" diff --git a/moose-core/python/moose/moose.py b/moose-core/python/moose/moose.py index c3c6b60db6cf3de1c49cd6d2e5516d7bc2b91d22..191d26d4cba4f87931c464f44d68d0240a10e361 100644 --- a/moose-core/python/moose/moose.py +++ b/moose-core/python/moose/moose.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- +from __future__ import print_function, division, absolute_import + # Author: Subhasis Ray # Maintainer: Dilawar Singh, Harsha Rani, Upi Bhalla -from __future__ import print_function, division, absolute_import import warnings import os import pydoc @@ -104,6 +105,7 @@ def loadModel(filename, modelpath, solverclass="gsl"): else: mu.error( "Unknown model extenstion '%s'" % extension) return None + # Version def version( ): @@ -154,7 +156,21 @@ def mooseReadSBML(filepath, loadpath, solver='ee',validate="on"): """ global sbmlImport_ if sbmlImport_: - return _readSBML.mooseReadSBML(filepath, loadpath, solver, validate) + modelpath = _readSBML.mooseReadSBML(filepath, loadpath, solver, validate) + sc = solver.lower() + if sc in ["gssa","gillespie","stochastic","gsolve"]: + method = "gssa" + elif sc in ["gsl","runge kutta","deterministic","ksolve","rungekutta","rk5","rkf","rk"]: + method = "gsl" + elif sc in ["exponential euler","exponentialeuler","neutral"]: + method = "ee" + else: + method = "ee" + + if method != 'ee': + chemError = _chemUtil.add_Delete_ChemicalSolver.mooseAddChemSolver(modelpath[0].path, method) + + return modelpath else: print( sbmlError_ ) return False @@ -652,3 +668,7 @@ def doc(arg, inherited=True, paged=True): pager(text) else: print(text) + + +# +# moose.py ends here diff --git a/moose-core/python/moose/server.py b/moose-core/python/moose/server.py index 400058a39e6b21c2d4cbee6de296046d4d940abd..9a9820e6809e734d6d64411fcc9c37493ea95dd8 100644 --- a/moose-core/python/moose/server.py +++ b/moose-core/python/moose/server.py @@ -1,5 +1,11 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, division +from __future__ import print_function, division, absolute_import + +# This is moose.server. +# It accepts simulation request on a specified TCP port (default 31417). +# It simulates the given file (usually a archive file e.g., tar.bz2) and sends +# back artefacts generated by simulation (mostly images); and streams data from +# moose.Tables back to client. __author__ = "Dilawar Singh" __copyright__ = "Copyright 2019, Dilawar Singh" @@ -12,18 +18,15 @@ import sys import re import os import time +import math import shutil import socket import signal import tarfile import tempfile import threading -import datetime -import subprocess import logging - -# setup environment variables for the streamer starts working. -os.environ['MOOSE_SOCKET_STREAMER_ADDRESS'] = 'ghevar.ncbs.res.in:31416' +import subprocess # create a logger for this server. logging.basicConfig( @@ -34,7 +37,7 @@ logging.basicConfig( filemode='a' ) console = logging.StreamHandler() -console.setLevel(logging.INFO) +console.setLevel(logging.DEBUG) formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') console.setFormatter(formatter) _logger = logging.getLogger('') @@ -42,20 +45,54 @@ _logger.addHandler(console) __all__ = [ 'serve' ] +# Global variable to stop all running threads. stop_all_ = False +sock_ = None +stop_streamer_ = {} -def handler(signum, frame): - global stop_all_ - _logger.info( "User terminated all processes." ) - stop_all_ = True +# Use prefixL_ bytes to encode the size of stream. One can probably use just one +# byte to do. Lets go with the inefficient one for now. +prefixL_ = 9 + +# Matplotlib text for running simulation. It make sures at each figure is saved +# to individual png files. +matplotlibText = """ +print( '>>>> saving all figues') +import matplotlib.pyplot as plt +def multipage(filename, figs=None, dpi=200): + pp = PdfPages(filename) + if figs is None: + figs = [plt.figure(n) for n in plt.get_fignums()] + for fig in figs: + fig.savefig(pp, format='pdf') + pp.close() + +def saveall(prefix='results', figs=None): + if figs is None: + figs = [plt.figure(n) for n in plt.get_fignums()] + for i, fig in enumerate(figs): + outfile = '%s.%d.png' % (prefix, i) + fig.savefig(outfile) + print( '>>>> %s saved.' % outfile ) + plt.close() -signal.signal( signal.SIGINT, handler) +try: + saveall() +except Exception as e: + print( '>>>> Error in saving: %s' % e ) + quit(0) +""" -def split_data( data ): - prefixLenght = 10 - return data[:prefixLenght].strip(), data[prefixLenght:] def execute(cmd): + """execute: Execute a given command. + + :param cmd: string, given command. + + Return: + ------ + Return a iterator over output. + """ popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True) for stdout_line in iter(popen.stdout.readline, ""): yield stdout_line @@ -64,52 +101,82 @@ def execute(cmd): if return_code: raise subprocess.CalledProcessError(return_code, cmd) -def send_msg(msg, conn): + +def find_files( dirname, ext=None, name_contains=None, text_regex_search=None): + files = [] + for d, sd, fs in os.walk(dirname): + for f in fs: + fpath = os.path.join(d,f) + include = True + if ext is not None: + if f.split('.')[-1] != ext: + include = False + if name_contains: + if name_contains not in os.path.basename(f): + include = False + if text_regex_search: + with open(fpath, 'r' ) as f: + txt = f.read() + if re.search(text_regex_search, txt) is None: + include = False + if include: + files.append(fpath) + return files + +def prefix_data_with_size(data): + global prefixL_ + prefix = b'0'*(prefixL_-int(math.log10(len(data)))-1) + b'%d' % len(data) + assert len(prefix) == prefixL_ + return b'%s%s' % (prefix, data) + +# Signal handler. +def signal_handler(signum, frame): + global stop_all_ + global sock_ + _logger.info( "User terminated all processes." ) + stop_all_ = True + # sock_.shutdown( socket.SHUT_RDWR ) + sock_.close() + time.sleep(1) + quit(1) + + +def split_data( data ): + global prefixL_ + return data[:prefixL_].strip(), data[prefixL_:] + +def send_msg(msg, conn, prefix='LOG'): if not msg.strip(): return False - _logger.debug( msg ) - msg = '%s>>> %s' % (socket.gethostname(), msg) - conn.sendall( b'%010d%s' % (len(msg), msg)) + if prefix != 'TAB': + _logger.debug(msg) + else: + _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 ) oldCWD = os.getcwd() if cwd is not None: os.chdir(cwd) - _logger.debug( "Executing in %s" % os.getcwd() ) - for line in execute(cmd.split()): - send_msg(line, conn) + try: + for line in execute(cmd.split()): + if line: + send_msg(line, conn) + except Exception as e: + send_msg("Simulation failed: %s" % e, conn) os.chdir(oldCWD) -def find_files_to_run( files ): - """Any file name starting with __main is to be run.""" - toRun = [] - for f in files: - if '__main' in os.path.basename(f): - toRun.append(f) - if toRun: - return toRun - - # Then guess. - if len(files) == 1: - return files - - for f in files: - with open(f, 'r' ) as fh: - txt = fh.read() - if re.search(r'def\s+main\(', txt): - if re.search('^\s+main\(\S+?\)', txt): - toRun.append(f) - return toRun - def recv_input(conn, size=1024): # first 10 bytes always tell how much to read next. Make sure the submit job # script has it - d = conn.recv(10, socket.MSG_WAITALL) - while len(d) < 10: + d = conn.recv(prefixL_, socket.MSG_WAITALL) + while len(d) < prefixL_: try: - d = conn.recv(10, socket.MSG_WAITALL) + d = conn.recv(prefixL_, socket.MSG_WAITALL) except Exception: - _logger.error( "Error in format. First 6 bytes are size of msg." ) + _logger.error("MSG FORMAT: %d bytes are size of msg."%prefixL_) continue d, data = int(d), b'' while len(data) < d: @@ -122,7 +189,7 @@ def writeTarfile( data ): _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.2) + time.sleep(0.1) if not tarfile.is_tarfile(tfile): _logger.warn( 'Not a valid tar file: %s' % tfile) return None @@ -133,40 +200,65 @@ def suffixMatplotlibStmt( filename ): with open(filename, 'r') as f: txt = f.read() - matplotlibText = ''' -# from matplotlib.backends.backend_pdf import PdfPages -import matplotlib.pyplot as plt - -def multipage(filename, figs=None, dpi=200): - pp = PdfPages(filename) - if figs is None: - figs = [plt.figure(n) for n in plt.get_fignums()] - for fig in figs: - fig.savefig(pp, format='pdf') - pp.close() - -def saveall(prefix='results', figs=None): - if figs is None: - figs = [plt.figure(n) for n in plt.get_fignums()] - for i, fig in enumerate(figs): - fig.savefig('%s.%d.png' %(prefix,i) ) - plt.close() - -try: - # multipage("results.pdf") - saveall() -except Exception as e: - print( e ) - ''' with open(outfile, 'w' ) as f: f.write( txt ) + f.write( '\n' ) f.write( matplotlibText ) return outfile +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 ) + while not os.path.exists( socketPath ): + #print( 'socket %s is not available yet.' % socketPath ) + time.sleep(0.1) + stop = stop_streamer_[threading.currentThread().name] + if stop: + return + + stClient = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + try: + stClient.connect(socketPath) + except socket.error as 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." ) + stClient.settimeout(0.05) + send_msg( b'Now streaming table data.', conn, 'TAB') + while not stop: + stop = stop_streamer_[threading.currentThread().name] + data = b'' + try: + data = stClient.recv(1024) + if len(data.strip()) > 0: + send_msg(data, conn, 'TAB') + except socket.timeout: + continue + stClient.close() + if os.path.isfile(socketPath): + os.unlink(socketPath) + def run_file(filename, conn, cwd=None): + # set environment variable so that socket streamer can start. + global stop_streamer_ + socketPath = os.path.join(tempfile.mkdtemp(), 'SOCK_TABLE_STREAMER') + os.environ['MOOSE_STREAMER_ADDRESS'] = socketPath + streamerThread = threading.Thread(target=streamer_client + , args=(socketPath, conn,)) + stop_streamer_[streamerThread.name] = False + streamerThread.daemon = True + streamerThread.start() filename = suffixMatplotlibStmt(filename) run( "%s %s" % (sys.executable, filename), conn, cwd) - _logger.info( '.... DONE' ) + stop_streamer_[streamerThread.name] = True + streamerThread.join( timeout = 1) + if streamerThread.is_alive(): + _logger.error( "The socket streamer client is still running...") def extract_files(tfile, to): userFiles = [] @@ -177,11 +269,9 @@ def extract_files(tfile, to): except Exception as e: _logger.warn( e) # now check if all files have been extracted properly - success = True for f in userFiles: if not os.path.exists(f): _logger.error( "File %s could not be extracted." % f ) - success = False return userFiles def prepareMatplotlib( cwd ): @@ -189,30 +279,48 @@ def prepareMatplotlib( cwd ): f.write( 'interactive : True' ) def send_bz2(conn, data): - data = b'%010d%s' % (len(data), data) - conn.sendall(data) + global prefixL_ + send_msg(data, conn, 'TAR') -def sendResults(tdir, conn, fromThisTime): +def sendResults(tdir, conn, notTheseFiles): # Only send new files. resdir = tempfile.mkdtemp() resfile = os.path.join(resdir, 'results.tar.bz2') - with tarfile.open( resfile, 'w|bz2') as tf: - for d, sd, fs in os.walk(tdir): - for f in fs: - fpath = os.path.join(d,f) - if datetime.datetime.fromtimestamp(os.path.getmtime(fpath)) > fromThisTime: - _logger.info( "Adding file %s" % f ) - tf.add(os.path.join(d, f), f) + for f in find_files(tdir, ext='png'): + _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 in result: %d' % len(data)) + _logger.info( 'Total bytes to send to client: %d' % len(data)) send_bz2(conn, data) shutil.rmtree(resdir) +def find_files_to_run( files ): + """Any file name starting with __main is to be run. + Many such files can be recieved by client. + """ + toRun = [] + for f in files: + if '__main' in os.path.basename(f): + toRun.append(f) + if toRun: + return toRun + # Else guess. + if len(files) == 1: + return files + + for f in files: + with open(f, 'r' ) as fh: + txt = fh.read() + if re.search(r'def\s+main\(', txt): + if re.search('^\s+main\(\S+?\)', txt): + toRun.append(f) + return toRun + def simulate( tfile, conn ): """Simulate a given tar file. """ @@ -240,49 +348,56 @@ def savePayload( conn ): def handle_client(conn, ip, port): isActive = True + _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." ) isActive = False - _logger.info( "PAYLOAD RECIEVED: %d" % nBytes ) if not os.path.isfile(tarfileName): send_msg("[ERROR] %s is not a valid tarfile. Retry"%tarfileName, conn) break - startSimTime = datetime.datetime.now() + + # list of files before the simulation. + notthesefiles = find_files(os.path.dirname(tarfileName)) res, msg = simulate( tarfileName, conn ) - if not res: + if 0 != res: send_msg( "Failed to run simulation: %s" % msg, conn) isActive = False time.sleep(0.1) - send_msg('>DONE SIMULATION', conn) + # Send results after DONE is sent. - sendResults(os.path.dirname(tarfileName), conn, startSimTime) + send_msg('All done', conn, 'EOS') + sendResults(os.path.dirname(tarfileName), conn, notthesefiles) + break + def start_server( host, port, max_requests = 10 ): global stop_all_ - soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + global sock_ + sock_ = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock_.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: - soc.bind( (host, port)) + sock_.bind( (host, port)) _logger.info( "Server created %s:%s" %(host,port) ) except Exception as e: _logger.error( "Failed to bind: %s" % e) quit(1) - # listen upto 100 of requests - soc.listen(max_requests) + # listen upto 10 of requests + sock_.listen(max_requests) while True: - conn, (ip, port) = soc.accept() - _logger.info( "Connected with %s:%s" % (ip, port) ) - try: - t = threading.Thread(target=handle_client, args=(conn, ip, port)) - t.start() - except Exception as e: - _logger.warn(e) if stop_all_: break - soc.close() + sock_.settimeout(10) + try: + conn, (ip, port) = sock_.accept() + except socket.timeout as e: + continue + sock_.settimeout(0.0) + t = threading.Thread(target=handle_client, args=(conn, ip, port)) + t.start() + sock_.close() def serve(host, port): start_server(host, port) @@ -290,11 +405,9 @@ def serve(host, port): def main( args ): global stop_all_ host, port = args.host, args.port - try: - serve(host, port) - except KeyboardInterrupt: - stop_all_ = True - quit(1) + # Install a signal handler. + signal.signal( signal.SIGINT, signal_handler) + serve(host, port) if __name__ == '__main__': import argparse diff --git a/moose-core/python/moose/streamer_utils.py b/moose-core/python/moose/streamer_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..a89f05025cc54e6d13fe0a55043d702a96df3d4b --- /dev/null +++ b/moose-core/python/moose/streamer_utils.py @@ -0,0 +1,58 @@ +"""streamer_utils.py: + +Utility for Table streamer. + +""" + +__author__ = "Dilawar Singh" +__copyright__ = "Copyright 2017-, Dilawar Singh" +__version__ = "1.0.0" +__maintainer__ = "Dilawar Singh" +__email__ = "dilawars@ncbs.res.in" +__status__ = "Development" + +import sys +import os +import numpy as np +import math +import struct +from collections import defaultdict + +def bytes_to_np_arr(data): + return np.frombuffer(data, float) + +def np_array_to_string(arr): + return np.uint8(arr).tostring() + +def np_array_to_data(arr): + # Make sure that first char of arr is 'H' + n = 0 + res = defaultdict(list) + while n < len(arr): + assert chr(int(arr[n])) == 'H', 'Expected H, Got %s'%arr[n] + hSize = int(arr[n+1]) + n += 1 + colName = np_array_to_string(arr[n+1:n+1+hSize]) + n += hSize + 1 + assert chr(int(arr[n])) == 'V', 'Expected V' + n += 1 + dataSize = int(arr[n]) + n += 1 + res[colName].append(arr[n:n+dataSize]) + n += dataSize + return { k : np.concatenate(v) for k, v in res.items() } + +def decode_data(data): + arr = bytes_to_np_arr(data) + assert int(arr[0]) == ord('H'), "First char must be H" + return np_array_to_data(arr) + +def test(): + with open(sys.argv[1], 'rb') as f: + data = f.read() + print( "[INFO ] Total bytes read %d" % len(data)) + s = decode_data(data) + print(s) + +if __name__ == '__main__': + test() diff --git a/moose-core/python/moose/utils.py b/moose-core/python/moose/utils.py index 64fdee8be38591246e6162cce2c658bce0a1b527..17c262d81c1b6637512525585af2080ab4270fa6 100644 --- a/moose-core/python/moose/utils.py +++ b/moose-core/python/moose/utils.py @@ -338,8 +338,10 @@ def autoposition(root): def loadModel(filename, target,method='ee'): moose.loadModel(filename,target) - moose.mooseaddChemSolver(target,method) - + moose.mooseAddChemSolver(target,method) + if moose.exists(target+'/kinetics/info'): + moose.element(target+'/kinetics/info').solver = method + def readcell_scrambled(filename, target, method='ee'): """A special version for handling cases where a .p file has a line with specified parent yet to be defined. diff --git a/moose-core/python/setup.cmake.py b/moose-core/python/setup.cmake.py index c7254b402121afb4ebe16364f81c2c3204914599..ca67559d45c7a172b395eb03fdf937fcdf69da4d 100644 --- a/moose-core/python/setup.cmake.py +++ b/moose-core/python/setup.cmake.py @@ -59,5 +59,5 @@ setup( ], install_requires = [ 'numpy' ], package_dir = { 'moose' : 'moose', 'rdesigneur' : 'rdesigneur' }, - package_data = { 'moose' : ['_moose' + suffix, 'neuroml2/schema/NeuroMLCoreDimensions.xml'] }, + package_data = { 'moose' : ['_moose' + suffix, 'neuroml2/schema/NeuroMLCoreDimensions.xml','chemUtil/rainbow2.pkl'] }, ) diff --git a/moose-core/scheduling/Clock.cpp b/moose-core/scheduling/Clock.cpp index dd60aecf359bf4fc8698a40031a1f0fb2f6fc4a8..e407bb63883ae39be46dc6eb7a15d2fa81be2d0b 100644 --- a/moose-core/scheduling/Clock.cpp +++ b/moose-core/scheduling/Clock.cpp @@ -425,8 +425,8 @@ const Cinfo* Clock::initCinfo() " Ksolve 16 0.1\n" " Stats 17 0.1\n" " Table2 18 1\n" - " SocketStreamer 19 5\n" - " Streamer 20 10\n" + " SocketStreamer 19 1\n" + " Streamer 20 5\n" " HDF5DataWriter 30 1\n" " HDF5WriterBase 30 1\n" @@ -1030,8 +1030,8 @@ void Clock::buildDefaultTick() defaultDt_[16] = 0.1; defaultDt_[17] = 0.1; defaultDt_[18] = 1; // For tables for chemical calculations. - defaultDt_[19] = 5; // For Socket Streamer - defaultDt_[20] = 10; // For CSV Streamer + defaultDt_[19] = 1; // For Socket Streamer + defaultDt_[20] = 5; // For CSV Streamer // 20-29 are not assigned. defaultDt_[30] = 1; // For the HDF writer diff --git a/moose-core/shell/Shell.cpp b/moose-core/shell/Shell.cpp index 1043f19d8b64af5eb157414ed15dd49b4a750603..fe55a708fe56d1cc53a3645611debd9de4a89f78 100644 --- a/moose-core/shell/Shell.cpp +++ b/moose-core/shell/Shell.cpp @@ -22,6 +22,7 @@ #include "../msg/OneToAllMsg.h" #include "../msg/SparseMsg.h" #include "../builtins/SocketStreamer.h" +#include "../builtins/Streamer.h" #include "Shell.h" #include "Wildcard.h" @@ -63,73 +64,68 @@ const Cinfo* Shell::initCinfo() &Shell::setCwe, &Shell::getCwe ); -//////////////////////////////////////////////////////////////// -// Dest Finfos: Functions handled by Shell -//////////////////////////////////////////////////////////////// + + // Dest Finfos: Functions handled by Shell static DestFinfo handleUseClock( "useClock" - , "Deals with assignment of path to a given clock." - " Arguments: path, field, tick number. " - , new EpFunc4< Shell, string, string, unsigned int, unsigned int >(&Shell::handleUseClock ) - ); + , "Deals with assignment of path to a given clock." + " Arguments: path, field, tick number. " + , new EpFunc4< Shell, string, string, unsigned int, unsigned int >(&Shell::handleUseClock ) + ); static DestFinfo handleCreate( "create" - , "create( class, parent, newElm, name, numData, isGlobal )" - , new EpFunc6<Shell, string, ObjId, Id, string, NodeBalance, unsigned int>(&Shell::handleCreate) - ); + , "create( class, parent, newElm, name, numData, isGlobal )" + , new EpFunc6<Shell, string, ObjId, Id, string, NodeBalance, unsigned int>(&Shell::handleCreate) + ); static DestFinfo handleDelete( "delete" - , "When applied to a regular object, this function operates " - "on the Id (element) specified by the ObjId argument. " - "The function deletes the entire object " - "array on this Id, including all dataEntries on it," - "all its messages, and all its children. The DataIndex here " - "is ignored, and all dataEntries are destroyed. \n" - "When applied to a message: Destroys only that one specific " - "message identified by the full ObjId. \n" - "Args: ObjId\n" - , new EpFunc1< Shell, ObjId >( & Shell::destroy ) - ); + , "When applied to a regular object, this function operates " + "on the Id (element) specified by the ObjId argument. " + "The function deletes the entire object " + "array on this Id, including all dataEntries on it," + "all its messages, and all its children. The DataIndex here " + "is ignored, and all dataEntries are destroyed. \n" + "When applied to a message: Destroys only that one specific " + "message identified by the full ObjId. \n" + "Args: ObjId\n" + , new EpFunc1< Shell, ObjId >( & Shell::destroy ) + ); static DestFinfo handleAddMsg( "addMsg" - , "Makes a msg. Arguments are:" - " msgtype, src object, src field, dest object, dest field" - , new EpFunc6< Shell, string, ObjId, string, ObjId, string, unsigned int >(&Shell::handleAddMsg) - ); + , "Makes a msg. Arguments are:" + " msgtype, src object, src field, dest object, dest field" + , new EpFunc6< Shell, string, ObjId, string, ObjId, string, unsigned int >(&Shell::handleAddMsg) + ); static DestFinfo handleQuit( "quit" - , "Stops simulation running and quits the simulator" - , new OpFunc0< Shell >( & Shell::handleQuit ) - ); + , "Stops simulation running and quits the simulator" + , new OpFunc0< Shell >( & Shell::handleQuit ) + ); + static DestFinfo handleMove( "move" - , "handleMove( Id orig, Id newParent ): " - "moves an Element to a new parent" - , new EpFunc2<Shell, Id, ObjId>( & Shell::handleMove ) - ); + , "handleMove( Id orig, Id newParent ): " + "moves an Element to a new parent" + , new EpFunc2<Shell, Id, ObjId>( & Shell::handleMove ) + ); static DestFinfo handleCopy( "copy" - , "handleCopy( vector< Id > args, string newName, unsigned int nCopies, bool toGlobal, bool copyExtMsgs ): " - " The vector< Id > has Id orig, Id newParent, Id newElm. " - "This function copies an Element and all its children to a new parent." - " May also expand out the original into nCopies copies." - " Normally all messages within the copy tree are also copied. " - " If the flag copyExtMsgs is true, then all msgs going out are also copied." - , new EpFunc5< Shell, vector< ObjId >, string, unsigned int, bool, bool >( - & Shell::handleCopy ) - ); + , "handleCopy( vector< Id > args, string newName, unsigned int nCopies, bool toGlobal, bool copyExtMsgs ): " + " The vector< Id > has Id orig, Id newParent, Id newElm. " + "This function copies an Element and all its children to a new parent." + " May also expand out the original into nCopies copies." + " Normally all messages within the copy tree are also copied. " + " If the flag copyExtMsgs is true, then all msgs going out are also copied." + , new EpFunc5< Shell, vector< ObjId >, string, unsigned int, bool, bool >( + & Shell::handleCopy ) + ); static DestFinfo setclock( "setclock" - , "Assigns clock ticks. Args: tick#, dt" - , new OpFunc2< Shell, unsigned int, double >( & Shell::doSetClock ) - ); + , "Assigns clock ticks. Args: tick#, dt" + , new OpFunc2< Shell, unsigned int, double >( & Shell::doSetClock ) + ); static Finfo* shellFinfos[] = { &setclock, - //////////////////////////////////////////////////////////////// - // Shared msg - //////////////////////////////////////////////////////////////// - // &master, - // &worker, &handleCreate, &handleDelete, &handleCopy, @@ -190,9 +186,8 @@ void Shell::setShellElement( Element* shelle ) * */ Id Shell::doCreate( string type, ObjId parent, string name, - unsigned int numData, - NodePolicy nodePolicy, - unsigned int preferredNode ) + unsigned int numData, NodePolicy nodePolicy, unsigned int preferredNode + ) { const Cinfo* c = Cinfo::find( type ); @@ -650,8 +645,8 @@ bool Shell::isRunning() const * This gets a bit complicated if the Element is a multidim array. */ void Shell::handleCreate( const Eref& e, - string type, ObjId parent, Id newElm, string name, - NodeBalance nb, unsigned int parentMsgIndex ) + string type, ObjId parent, Id newElm, string name, + NodeBalance nb, unsigned int parentMsgIndex ) { innerCreate( type, parent, newElm, name, nb, parentMsgIndex ); } @@ -702,8 +697,7 @@ bool Shell::adopt( Id parent, Id child, unsigned int msgIndex ) * Assumes we've already done all the argument checking. */ void Shell::innerCreate( string type, ObjId parent, Id newElm, string name, - const NodeBalance& nb, unsigned int msgIndex ) -// unsigned int numData, bool isGlobal + const NodeBalance& nb, unsigned int msgIndex ) { const Cinfo* c = Cinfo::find( type ); if ( c ) @@ -720,7 +714,6 @@ void Shell::innerCreate( string type, ObjId parent, Id newElm, string name, case MooseSingleNode: cout << "Error: Shell::innerCreate: Yet to implement SingleNodeDataElement. Making BlockBalance.\n"; ret = new LocalDataElement( newElm, c, name, nb.numData ); - // ret = new SingleNodeDataElement( newElm, c, name, numData, nb.preferredNode ); break; }; assert( ret ); @@ -728,9 +721,7 @@ void Shell::innerCreate( string type, ObjId parent, Id newElm, string name, ret->setTick( Clock::lookupDefaultTick( c->name() ) ); } else - { assert( 0 ); - } } void Shell::destroy( const Eref& e, ObjId oid) diff --git a/moose-core/tests/python/testDisabled_socket_streamer_tcp.py b/moose-core/tests/python/testDisabled_socket_streamer_tcp.py new file mode 100644 index 0000000000000000000000000000000000000000..7a9153f1b00002a07952c1fea538bb62bce372d0 --- /dev/null +++ b/moose-core/tests/python/testDisabled_socket_streamer_tcp.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +from __future__ import print_function, division + +__author__ = "Dilawar Singh" +__copyright__ = "Copyright 2016, Dilawar Singh" +__credits__ = ["NCBS Bangalore"] +__license__ = "GNU GPL" +__version__ = "1.0.0" +__maintainer__ = "Dilawar Singh" +__email__ = "dilawars@ncbs.res.in" +__status__ = "Development" + +import os +import sys +sys.path.append(os.path.dirname(__file__)) +import time +import socket +import numpy as np +import multiprocessing as mp +import moose +import moose.utils as mu +import models +from collections import defaultdict + +print( '[INFO] Using moose form %s' % moose.__file__ ) + +port_ = 3114 + +def socket_client(q, done): + global port_ + # This function waits for socket to be available. + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + while 1: + if 1 == done.value: + print( '[INFO] MOOSE is done before I could connect' ) + q.put({}) + return + s.settimeout(0.5) + try: + s.connect( ('127.0.0.1', port_) ) + except socket.timeout as e: + print(e) + break + + print( 'Py: Connected' ) + + # This is client reponsibility to read the data. + s.settimeout(0.01) + data = b'' + while True: + try: + data += s.recv(1024) + except socket.timeout: + print(end='x') + if 1 == done.value: + break + if data: + res = mu.decode_data(data) + q.put(res) + else: + q.put({}) + s.close() + return + +def test(): + global finish_all_ + os.environ['MOOSE_STREAMER_ADDRESS'] = 'http://127.0.0.1:%d'%port_ + done = mp.Value('d', 0) + q = mp.Queue() + client = mp.Process(target=socket_client, args=(q, done)) + client.start() + + time.sleep(0.1) + + print( '[INFO] Socket client is running now' ) + ts = models.simple_model_a() + moose.reinit() + time.sleep(0.1) + # If TCP socket is created, some delay is often neccessary before start. Don't + # know why. probably some latency in a fresh TCP socket. A TCP guru can + # tell. + moose.start(50) + print( 'MOOSE is done' ) + + time.sleep(0.5) + done.value = 1 + res = q.get() + client.join() + + if not res: + raise RuntimeWarning('Nothing was streamed') + for k in res: + a = res[k][1::2] + b = moose.element(k).vector + print(k, len(a), len(b)) + assert( (a==b).all()) + print( 'Test 1 passed' ) + +def main( ): + test( ) + print( '[INFO] All tests passed' ) + +if __name__ == '__main__': + main() diff --git a/moose-core/tests/python/test_socket_streamer_multitab.py b/moose-core/tests/python/test_socket_streamer_multitab.py new file mode 100644 index 0000000000000000000000000000000000000000..7f3fe8abe777303e3e7ee6451297c827f8cba877 --- /dev/null +++ b/moose-core/tests/python/test_socket_streamer_multitab.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 -*- +''' +Here is some docstring. +''' +from __future__ import print_function, division + +import os +import random +import time +import multiprocessing as mp +import socket +import moose +import json +import struct +import numpy as np +import moose.utils as mu + + +print('Using from %s. VERSION: %s' % (moose.__file__, moose.__version__)) + +sockPath = '/tmp/MOOSE' +os.environ['MOOSE_STREAMER_ADDRESS'] = 'file://%s' % sockPath + + +def streamer_handler(done, q): + # Streamer handler + global sockPath + global all_done_ + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + while not os.path.exists(sockPath): + time.sleep(0.01) + if done.value == 1: + break + s.connect( sockPath ) + print( "[INFO ] Connected" ) + s.settimeout(1e-5) + data = b'' + while True: + if done.value == 1: + break + try: + d = s.recv(10240) + if d.strip(): + data += d + except socket.timeout: + pass + + res = mu.decode_data(data) + q.put(res) + +def make_network(): + """ + This snippet sets up a recurrent network of IntFire objects, using + SimpleSynHandlers to deal with spiking events. + It isn't very satisfactory as activity runs down after a while. + It is a good example for using the IntFire, setting up random + connectivity, and using SynHandlers. + """ + global all_done_ + done = mp.Value('i', 0) + q = mp.Queue() + th = mp.Process(target=streamer_handler, args=(done, q)) + th.start() + + size = 1024 + dt = 0.2 + runsteps = 10 + delayMin = 0 + delayMax = 4 + weightMax = 1 + Vmax = 1.0 + thresh = 0.4 + refractoryPeriod = 0.4 + tau = 0.5 + connectionProbability = 0.01 + random.seed( 123 ) + np.random.seed( 456 ) + t0 = time.time() + + network = moose.IntFire( 'network', size ); + syns = moose.SimpleSynHandler( '/network/syns', size ); + moose.connect( syns, 'activationOut', network, 'activation', 'OneToOne' ) + moose.le( '/network' ) + syns.vec.numSynapses = [1] * size + sv = moose.vec( '/network/syns/synapse' ) + print('before connect t = %.3f'%(time.time() - t0)) + mid = moose.connect( network, 'spikeOut', sv, 'addSpike', 'Sparse') + print('after connect t = %.3f'%(time.time() - t0)) + #print mid.destFields + m2 = moose.element( mid ) + m2.setRandomConnectivity( connectionProbability, 5489 ) + print('after setting connectivity, t=%.3f'%(time.time() - t0)) + #network.vec.Vm = [(Vmax*random.random()) for r in range(size)] + network.vec.Vm = np.random.rand( size ) * Vmax + network.vec.thresh = thresh + network.vec.refractoryPeriod = refractoryPeriod + network.vec.tau = tau + numSynVec = syns.vec.numSynapses + print('Middle of setup, t = %.3f'%(time.time() - t0)) + numTotSyn = sum( numSynVec ) + print((numSynVec.size, ', tot = ', numTotSyn, ', numSynVec = ', numSynVec)) + for item in syns.vec: + h = moose.element(item) + h.synapse.delay = delayMin + (delayMax-delayMin) * np.random.rand(len(h.synapse)) + h.synapse.weight = np.random.rand(len(h.synapse)) * weightMax + print('After setup, t = %.3f'%(time.time()-t0)) + + numStats = 100 + stats = moose.SpikeStats( '/stats', numStats ) + stats.vec.windowLength = 1 # timesteps to put together. + plots = moose.Table( '/plot', numStats ) + convergence = size // numStats + for i in range( numStats ): + for j in range( size//numStats ): + k = i * convergence + j + moose.connect( network.vec[k], 'spikeOut', stats.vec[i], 'addSpike' ) + moose.connect( plots, 'requestOut', stats, 'getMean', 'OneToOne' ) + + t1 = time.time() + moose.reinit() + print('reinit time t = %.3f'%(time.time() - t1)) + network.vec.Vm = np.random.rand( size ) * Vmax + print('setting Vm , t = %.3f'%(time.time() - t1)) + t1 = time.time() + moose.start(runsteps * dt, 1) + time.sleep(0.1) + done.value = 1 + print('runtime, t = %.3f'%(time.time() - t1)) + print(network.vec.Vm[99:103], network.vec.Vm[900:903]) + res = q.get() + + for tabPath in res: + aWithTime = res[tabPath] + a = aWithTime[1::2] + b = moose.element(tabPath).vector + print( tabPath, len(a), len(b) ) + if len(a) == len(b): + assert np.equal(a, b).all() + else: + print( "Table did not equal size. The last table is allowed to " + " have fewer entries." ) + + + th.join() + print( 'All done' ) + +if __name__ == '__main__': + make_network() diff --git a/moose-core/tests/python/test_socket_streamer_tcp.py b/moose-core/tests/python/test_socket_streamer_tcp.py deleted file mode 100644 index 497b66fb0412df6a1e454dca10ed19775bb1a59f..0000000000000000000000000000000000000000 --- a/moose-core/tests/python/test_socket_streamer_tcp.py +++ /dev/null @@ -1,182 +0,0 @@ -# -*- coding: utf-8 -*- -"""test_socket_streamer.py: - - MOOSE must create a socket server on PORT 31616 (default) - or setup moose.SocketStreamer port to appropriate port number. - - Client can read data from this socket. -""" - -from __future__ import print_function - -__author__ = "Dilawar Singh" -__copyright__ = "Copyright 2016, Dilawar Singh" -__credits__ = ["NCBS Bangalore"] -__license__ = "GNU GPL" -__version__ = "1.0.0" -__maintainer__ = "Dilawar Singh" -__email__ = "dilawars@ncbs.res.in" -__status__ = "Development" - -import os -import sys -sys.path.append( os.path.dirname(__file__)) -import time -import socket -import numpy as np -import threading -import moose -import models -import json -from collections import defaultdict - -finish_all_ = False - -print( '[INFO] Using moose form %s' % moose.__file__ ) - -def get_msg(s, n=1024): - d = s.recv( n, socket.MSG_WAITALL) - while len(d) < n: - d += s.recv(n-len(d), socket.MSG_WAITALL) - return d - -def socket_client(host='127.0.0.1', port = 31416): - # This function waits for socket to be available. - global finish_all_ - s = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) - while 1: - if finish_all_: - print( '[INFO] MOOSE is done before I could connect' ) - break - # print('Py: Trying to connect to %s, %s' % (host, port)) - # print( end = '.' ) - try: - s.connect( ('', port) ) - break - except Exception as e: - continue - print(e, end = 'x') - sys.stdout.flush() - pass - - print( 'Connected' ) - if not finish_all_: - print( 'Py: Connected with socket.' ) - - # This is client reponsibility to read the data. - s.settimeout(0.1) - data = b'' - while not finish_all_: - try: - data += get_msg(s) - except socket.timeout as e: - print( e, end = ' ' ) - - s.close() - assert data, "No data streamed" - print( 'recieved data:\n', data ) - res = defaultdict(list) - for x in data.split(b'\n'): - if not x.strip(): - continue - d = json.loads(x) - for k, v in d.items(): - res[k] += v - - expected = {u'/compt/tabB/tabC': ([25.,1.07754388], [14.71960144, 0.16830373]) - , u'/compt/a/tab': ([25., 0.42467006], [14.71960144, 0.16766705]) - , u'/compt/tabB': ([25., 2.57797725], [14.71960144, 0.16842971]) - } - nd = {} - for k in res: - v = res[k] - nd[k] = (np.mean(v, axis=0), np.std(v, axis=0)) - assert np.isclose(expected[k], nd[k]).all(), \ - "Exptected %s, got %s" % (str(expected[k]), str(nd[k])) - -def sanity_test( ): - a = moose.Table( '/t1' ) - b = moose.Table( '/t1/t1' ) - c = moose.Table( '/t1/t1/t1' ) - st = moose.SocketStreamer( '/s' ) - st.addTable( a ) - assert( st.numTables == 1 ) - st.addTable( b ) - assert( st.numTables == 2 ) - st.addTable( c ) - assert( st.numTables == 3 ) - st.addTable( c ) - assert( st.numTables == 3 ) - st.addTable( c ) - assert( st.numTables == 3 ) - - st.removeTable( c ) - assert( st.numTables == 2 ) - st.removeTable( c ) - assert( st.numTables == 2 ) - st.removeTable( a ) - assert( st.numTables == 1 ) - st.removeTable( b ) - assert( st.numTables == 0 ) - st.removeTable( b ) - assert( st.numTables == 0 ) - print( 'Sanity test passed' ) - - st.addTables( [a, b, c ]) - assert st.numTables == 3 - st.removeTables( [a, a, c] ) - assert st.numTables == 1 - moose.delete( '/t1' ) - moose.delete( '/s' ) - -def test(): - global finish_all_ - os.environ['MOOSE_STREAMER_ADDRESS'] = 'http://127.0.0.1:31416' - client = threading.Thread(target=socket_client, args=()) - # client.daemon = True - client.start() - print( '[INFO] Socket client is running now' ) - tables = models.simple_model_a() - time.sleep(0.1) - moose.reinit() - moose.start(50) - print( 'MOOSE is done' ) - - # sleep for some time so data can be read. - time.sleep(1) - finish_all_ = True - client.join() - print( 'Test 1 passed' ) - -def test_without_env(): - global finish_all_ - os.environ['MOOSE_STREAMER_ADDRESS'] = '' - client = threading.Thread(target=socket_client, args=()) - #client.daemon = True - client.start() - print( '[INFO] Socket client is running now' ) - tables = create_model() - # Now create a streamer and use it to write to a stream - st = moose.SocketStreamer( '/compt/streamer' ) - st.address = 'http://127.0.0.1:31416' - assert st.port == 31416, "Got %s expected %s" % (st.port, expected) - st.addTable(tables[0]) - st.addTables(tables[1:]) - assert st.numTables == 3 - # Give some time for socket client to make connection. - moose.reinit() - moose.start(50) - time.sleep(1) - print( 'MOOSE is done' ) - # sleep for some time so data can be read. - finish_all_ = True - client.join() - print( 'Test 2 passed' ) - -def main( ): - test( ) - # test_without_env() - print( '[INFO] All tests passed' ) - -if __name__ == '__main__': - main() diff --git a/moose-core/tests/python/test_socket_streamer_uds.py b/moose-core/tests/python/test_socket_streamer_uds.py index 542c3a46b212cb98f8218816a66bd31afecb2beb..480af8c653d2367175796b7ce4815ab43ecb714f 100644 --- a/moose-core/tests/python/test_socket_streamer_uds.py +++ b/moose-core/tests/python/test_socket_streamer_uds.py @@ -24,101 +24,81 @@ sys.path.append(os.path.dirname(__file__)) import time import socket import numpy as np -import threading +import multiprocessing as mp import moose +import moose.utils as mu import json import models from collections import defaultdict -finish_all_ = False - print( '[INFO] Using moose form %s' % moose.__file__ ) sockFile_ = '/tmp/MOOSE' -def get_msg(s, n=1024): - d = s.recv(n) - while(len(d) < n): - d1 = s.recv(n-len(d)) - d += d1 - return d - -def socket_client( ): +def socket_client(done, q): # This function waits for socket to be available. - global finish_all_ global sockFile_ - s = socket.socket( socket.AF_UNIX, socket.SOCK_STREAM ) + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) address = sockFile_ - while 1: - if finish_all_: - print( '[INFO] MOOSE is done before I could connect' ) - break - # print('Py: Trying to connect to %s, %s' % (host, port)) - # print( end = '.' ) - try: - s.connect( address ) - break - except Exception as e: - pass - - if not finish_all_: - print( 'Py: Connected with socket.' ) + while not os.path.exists(address): + if done.value == 1: + return + continue + s.connect( address ) + print( 'Py: Connected with socket. %s' % sockFile_ ) # This is client reponsibility to read the data. + print( 'Py: Fetching...' ) data = b'' - s.settimeout(1) - while not finish_all_: + s.settimeout(0.01) + while True: try: - d = get_msg(s, 1024) - data += d - except socket.timeout as e: - pass - s.close() + data += s.recv(64) + except socket.timeout: + print('x', end = '' ) - assert data, "No data streamed" - res = defaultdict(list) - for x in data.split(b'\n'): - if not x.strip(): - continue - x = x.decode( 'utf8' ) - try: - d = json.loads(x) - except Exception as e: - print( data ) - raise e - for k, v in d.items(): - res[k] += v - - expected = {u'/compt/tabB/tabC': ([25.,1.07754388], [14.71960144, 0.16830373]) - , u'/compt/a/tab': ([25., 0.42467006], [14.71960144, 0.16766705]) - , u'/compt/tabB': ([25., 2.57797725], [14.71960144, 0.16842971]) - } - nd = {} - for k in res: - v = res[k] - nd[k] = (np.mean(v, axis=0), np.std(v, axis=0)) - assert np.isclose(expected[k], nd[k]).all(), \ - "Exptected %s, got %s" % (str(expected[k]), str(nd[k])) + if done.value == 1: + print( 'Simulation is over' ) + break + s.close() + if not data: + print("No data streamed") + done.value = 1 + q.put({}) + return + res = mu.decode_data(data) + q.put(res) def test(): - global finish_all_ - client = threading.Thread(target=socket_client, args=()) - #client.daemon = True + q = mp.Queue() + done = mp.Value( 'd', 0.0) + client = mp.Process(target=socket_client, args=(done, q)) client.start() print( '[INFO] Socket client is running now' ) - tables = models.simple_model_a() + time.sleep(0.1) + # Now create a streamer and use it to write to a stream - os.environ['MOOSE_STREAMER_ADDRESS'] = 'file:///tmp/moose' + os.environ['MOOSE_STREAMER_ADDRESS'] = 'file://%s' % sockFile_ + models.simple_model_a() # Give some time for socket client to make connection. moose.reinit() moose.start(50) time.sleep(0.1) - finish_all_ = True - print( 'MOOSE is done' ) + done.value = 1 + + res = q.get() + if not res: + raise RuntimeWarning( 'Nothing was streamed') + for k in res: + aWithTime = res[k] + a = aWithTime[1::2] + b = moose.element(k).vector + print(k, len(a), len(b)) + assert (a == b).all() + # sleep for some time so data can be read. client.join() - print( 'Test 2 passed' ) def main( ): test() diff --git a/moose-core/tests/python/test_streamer.py b/moose-core/tests/python/test_streamer.py index 43387df4b69d0fe08a6302bce55ee7af0d6e3ff9..3136ec24808beddb100f94a72901d2936088bdae 100644 --- a/moose-core/tests/python/test_streamer.py +++ b/moose-core/tests/python/test_streamer.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -"""test_streamer.py: +from __future__ import print_function +"""test_streamer.py: Test script for Streamer class. - """ __author__ = "Dilawar Singh" @@ -14,13 +14,17 @@ __maintainer__ = "Dilawar Singh" __email__ = "dilawars@ncbs.res.in" __status__ = "Development" -import os -import sys -import time import moose +import threading import numpy as np -print(( '[INFO] Using moose form %s' % moose.__file__ )) +import time +import os +import sys +print('[INFO] Using moose form %s' % moose.__file__) + +all_done_ = False +# Poll the file to see that we are really writing to it. def sanity_test( ): a = moose.Table( '/t1' ) b = moose.Table( '/t1/t1' ) @@ -64,6 +68,7 @@ def sanity_test( ): def test( ): compt = moose.CubeMesh( '/compt' ) + assert compt r = moose.Reac( '/compt/r' ) a = moose.Pool( '/compt/a' ) a.concInit = 1 @@ -77,6 +82,10 @@ def test( ): 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' ) @@ -88,21 +97,22 @@ def test( ): # Now create a streamer and use it to write to a stream st = moose.Streamer( '/compt/streamer' ) - st.outfile = os.path.join( os.getcwd(), 'temp.npy' ) - print(("outfile set to: %s " % st.outfile )) - assert st.outfile == os.path.join( os.getcwd(), 'temp.npy' ), st.outfile + st.outfile = outfile + print("outfile set to: %s " % st.outfile ) st.addTable( tabA ) st.addTables( [ tabB, tabC ] ) - assert st.numTables == 3 moose.reinit( ) - print( '[INFO] Running for 57 seconds' ) - moose.start( 57 ) + 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: @@ -110,11 +120,9 @@ def test( ): else: data = np.load( outfile ) # Total rows should be 58 (counting zero as well). - # print(data) + # print(data) # print( data.dtype ) - time = data['time'] - print( time ) - assert data.shape >= (58,), data.shape + assert data.shape >= (101,), data.shape print( '[INFO] Test 2 passed' ) return 0 diff --git a/moose-core/tests/python/test_table_streaming_support.py b/moose-core/tests/python/test_table_streaming_support.py index 0747ef66d52a300c84bafb00a7de143aee996fa6..42b0b83b8e002ce04276708da97b9f3d3f682b77 100644 --- a/moose-core/tests/python/test_table_streaming_support.py +++ b/moose-core/tests/python/test_table_streaming_support.py @@ -59,7 +59,7 @@ def test( ): moose.reinit( ) [ print_table( x) for x in [tabA, tabB, tabC] ] runtime = 1000 - print( 'Starting moose for %s' % runtime ) + print( 'Starting moose for %d secs' % runtime ) moose.start( runtime, 1 ) print( ' MOOSE is done' ) diff --git a/moose-core/utility/Annotator.cpp b/moose-core/utility/Annotator.cpp index 4f1c51bd307243573f13f2277c56e4083226c08c..f2df4aa1fa2dd436f4e5937e1321fa213a970101 100644 --- a/moose-core/utility/Annotator.cpp +++ b/moose-core/utility/Annotator.cpp @@ -32,7 +32,18 @@ const Cinfo* Annotator::initCinfo() &Annotator::setZ, &Annotator::getZ ); - + static ValueFinfo< Annotator, double > width( + "width", + "width field. Typically display width", + &Annotator::setwidth, + &Annotator::getwidth + ); + static ValueFinfo< Annotator, double > height( + "height", + "height field. Typically display height", + &Annotator::setheight, + &Annotator::getheight + ); static ValueFinfo< Annotator, string > notes( "notes", "A string to hold some text notes about parent object", @@ -91,6 +102,8 @@ const Cinfo* Annotator::initCinfo() &x, // Value &y, // Value &z, // Value + &width, + &height, ¬es, // Value &color, // Value &textColor, // Value @@ -116,7 +129,7 @@ const Cinfo* Annotator::initCinfo() static const Cinfo* annotatorCinfo = Annotator::initCinfo(); Annotator::Annotator() - : x_( 0.0 ), y_( 0.0 ), z_( 0.0 ), + : x_( 0.0 ), y_( 0.0 ), z_( 0.0 ), width_( 0.0 ), height_( 0.0 ), notes_( "" ), color_( "white" ), textColor_( "black" ), icon_( "sphere" ),solver_( "ee"),runtime_(100.0),dirpath_(""),modeltype_("") { @@ -153,6 +166,26 @@ void Annotator::setZ( double v ) z_ = v; } +double Annotator::getheight() const +{ + return height_; +} + +void Annotator::setheight( double v ) +{ + height_ = v; +} + +double Annotator::getwidth() const +{ + return width_; +} + +void Annotator::setwidth( double v ) +{ + width_ = v; +} + string Annotator::getNotes() const { return notes_; diff --git a/moose-core/utility/Annotator.h b/moose-core/utility/Annotator.h index 8dd4e5d5b78bac44dcaf07a52a57118cb56e94fd..759a6aaa220bc86f6c8d19dd65a8876307a59dc9 100644 --- a/moose-core/utility/Annotator.h +++ b/moose-core/utility/Annotator.h @@ -23,6 +23,10 @@ class Annotator double getY() const; void setY( double v ); double getZ() const; + void setwidth( double v ); + double getwidth() const; + void setheight( double v ); + double getheight() const; void setZ( double v ); string getNotes() const; void setNotes( string v ); @@ -45,6 +49,8 @@ class Annotator double x_; double y_; double z_; + double height_; + double width_; string notes_; string color_; string textColor_; diff --git a/moose-core/utility/CMakeLists.txt b/moose-core/utility/CMakeLists.txt index 5fcd5cee46b044817d95d7c8c99095b7f37d6b4f..00c014f1182196268847e3275b0633d98d44fa8b 100644 --- a/moose-core/utility/CMakeLists.txt +++ b/moose-core/utility/CMakeLists.txt @@ -16,5 +16,6 @@ add_library(utility Annotator.cpp Vec.cpp cnpy.cpp + fileutils.cpp #matrix_util.cpp ) diff --git a/moose-core/utility/fileutils.cpp b/moose-core/utility/fileutils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..221eb165cba126fef9fdf69795e2872315a2651e --- /dev/null +++ b/moose-core/utility/fileutils.cpp @@ -0,0 +1,26 @@ +/* + * Description: Filesystem related utilities. + * + * Author: Dilawar Singh (), dilawars@ncbs.res.in + * Organization: NCBS Bangalore + */ + +#include <string> +#include <map> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> + +using namespace std; + +#include "utility.h" + +namespace moose { + +bool filepath_exists( const string& path ) +{ + struct stat buffer; + return (stat (path.c_str(), &buffer) == 0); +} + +} //namespace moose. diff --git a/moose-core/utility/print_function.hpp b/moose-core/utility/print_function.hpp index c2cf0c7fee54a0535a052261ee069e5bb5bb786a..9d389a0669f17c43cf3606715bd0cf621beac3c3 100644 --- a/moose-core/utility/print_function.hpp +++ b/moose-core/utility/print_function.hpp @@ -95,7 +95,6 @@ namespace moose { string mapToString(const map<A, B>& m, bool value=true) { unsigned int width = 81; - unsigned int mapSize = m.size(); unsigned int size = 0; vector<string> row; @@ -250,7 +249,9 @@ namespace moose { #ifdef NDEBUG #define LOG(t, a ) ((void)0); #else /* ----- not NDEBUG ----- */ -#define LOG(t, a) { stringstream __ss__; __ss__ << a; moose::__dump__(__ss__.str(), t ); } +#define LOG(t, a) { stringstream __ss__; \ + __ss__ << __func__ << ": " << a; moose::__dump__(__ss__.str(), t); \ +} #endif /* ----- not NDEBUG ----- */ /*----------------------------------------------------------------------------- diff --git a/moose-core/utility/setupenv.cpp b/moose-core/utility/setupenv.cpp index 422d3471d234fe051b1fcbb6d7c8f8f744ad176c..088f84f4f5ba7e5ded9e1ecf8e3c42b3d0d6a415 100644 --- a/moose-core/utility/setupenv.cpp +++ b/moose-core/utility/setupenv.cpp @@ -33,6 +33,7 @@ #include <string> #include <sstream> #include <cstdlib> + using namespace std; extern unsigned getNumCores(); diff --git a/moose-core/utility/simple_assert.hpp b/moose-core/utility/simple_assert.hpp index 78c41d8fd65969343c30fe95b58588664a3c15a7..857ad72003a811d507d0982a4447b46b21f9141f 100644 --- a/moose-core/utility/simple_assert.hpp +++ b/moose-core/utility/simple_assert.hpp @@ -62,16 +62,12 @@ namespace moose // SIMPLE_ASSERT_MSG // //--------------------------------------------------------------------------------------// -# undef SIMPLE_ASSERT_MSG +#undef SIMPLE_ASSERT_MSG #if defined(SIMPLE_DISABLE_ASSERTS) || defined(NDEBUG) - #define SIMPLE_ASSERT_MSG(expr, msg) ((void)0) - #elif defined(SIMPLE_ENABLE_ASSERT_HANDLER) - #include "current_function.hpp" - namespace moose { void assertion_failed_msg(char const * expr, char const * msg, diff --git a/moose-core/utility/strutil.h b/moose-core/utility/strutil.h index 4d95c1419f2ce8fd197dc6ef0c292809f7c4be68..e2480d2bdb8fc4f81d8950f14e9a750588e88365 100644 --- a/moose-core/utility/strutil.h +++ b/moose-core/utility/strutil.h @@ -9,6 +9,7 @@ #define _STRINGUTIL_H #include <string> +#include <sstream> #include <vector> namespace moose diff --git a/moose-core/utility/testing_macros.hpp b/moose-core/utility/testing_macros.hpp index 87189889de3cc029dcd1bbded2972f5a6a693773..30359ef9a54b3638501be6d6d9715993c38bf405 100644 --- a/moose-core/utility/testing_macros.hpp +++ b/moose-core/utility/testing_macros.hpp @@ -68,7 +68,7 @@ static ostringstream assertStream; } #define EXPECT_NEQ(a, b, token) \ - if( (a) == (b)) { \ + if( ! ((a) != (b)) ) { \ assertStream.str(""); \ LOCATION(assertStream); \ assertStream << "Not expected " << a << endl; \ @@ -77,7 +77,7 @@ static ostringstream assertStream; } #define EXPECT_GT(a, b, token) \ - if( (a) <= (b)) { \ + if( !((a) > (b)) ) { \ assertStream.str(""); \ LOCATION(assertStream); \ assertStream << "Expected greater than " << a << ", received " << b << endl; \ @@ -86,7 +86,7 @@ static ostringstream assertStream; } #define EXPECT_GTE(a, b, token) \ - if( (a) < (b)) { \ + if( !((a) >= (b)) ) { \ assertStream.str(""); \ LOCATION(assertStream); \ assertStream << "Expected greater than or equal to " << a \ @@ -96,7 +96,7 @@ static ostringstream assertStream; } #define EXPECT_LT(a, b, token) \ - if( (a) >= (b)) { \ + if( ! ((a) < (b)) ) { \ assertStream.str(""); \ LOCATION(assertStream); \ assertStream << "Expected less than " << a << ", received " << b << endl; \ @@ -105,7 +105,7 @@ static ostringstream assertStream; } #define EXPECT_LTE(a, b, token) \ - if( (a) < (b)) { \ + if( ! ((a) <= (b)) ) { \ assertStream.str(""); \ LOCATION(assertStream); \ assertStream << "Expected less than or equal to " << a \ @@ -129,13 +129,6 @@ static ostringstream assertStream; throw std::runtime_error(assertStream.str()); \ } -#define ASSERT_LT( a, b, msg) \ - EXPECT_LT(a, b, msg); \ - assertStream.str(""); \ - assertStream.precision( 9 ); \ - assertStream << msg; \ - throw std::runtime_error( assertStream.str() ); \ - #define ASSERT_EQ(a, b, token) \ if( ! doubleEq((a), (b)) ) { \ assertStream.str(""); \ @@ -165,5 +158,4 @@ static ostringstream assertStream; throw std::runtime_error(assertStream.str()); \ } - #endif /* ----- #ifndef TESTING_MACROS_INC ----- */ diff --git a/moose-core/utility/utility.h b/moose-core/utility/utility.h index ee0e88d7f1743de877bc0d09981b4d6c03c2e61c..33fae6764c4a6e9a639c11bd3c29243f3121b55b 100644 --- a/moose-core/utility/utility.h +++ b/moose-core/utility/utility.h @@ -25,6 +25,9 @@ namespace moose const map<std::string, std::string>& getArgMap(); string getEnv( const string& env); + // In fileutils.cpp + bool filepath_exists( const string& path ); + /** * @brief Givem path of MOOSE element, return its name. It's behaviour is * like `basename` of unix command e.g. /a/b/c --> c diff --git a/moose-examples/README.md b/moose-examples/README.md index d6f12fbe4dc877e6f508dbe33d5469f948192e04..b5e52fa0897f306ecb174862d165d809bfe4a355 100644 --- a/moose-examples/README.md +++ b/moose-examples/README.md @@ -1,3 +1,13 @@ [](https://travis-ci.org/BhallaLab/moose-examples). Examples, tutorial and demo scripts. + +# How to run + +First install `pymoose` from PyPI using `pip`. + + $ pip install pymoose --pre --user # get the latest version + +Make sure that you have `matplotlib`, and `scipy` (optional) installed as well. + +Some scripts might require `PyQt4` or `PyQt5` as well. diff --git a/moose-examples/tutorials/Rdesigneur/chem/CICRspineDend.g b/moose-examples/tutorials/Rdesigneur/chem/CICRspineDend.g index b1211065ca35829879aec98342c812903f3f0a30..f57401f53f47f1232c00e36b963af0df5c67553f 100644 --- a/moose-examples/tutorials/Rdesigneur/chem/CICRspineDend.g +++ b/moose-examples/tutorials/Rdesigneur/chem/CICRspineDend.g @@ -1,7 +1,7 @@ //genesis // kkit Version 11 flat dumpfile -// Saved on Tue Aug 21 06:59:07 2018 +// Saved on Sat Feb 16 15:40:53 2019 include kkit {argv 1} @@ -141,7 +141,7 @@ simundump kpool /kinetics/ActIP3R 0 0.0 0 0 0 0 0 0 1866.9 0 \ simundump text /kinetics/ActIP3R/notes 0 "" call /kinetics/ActIP3R/notes LOAD \ "" -simundump kchan /kinetics/ActIP3R/chan 0 8000 0.1 0 1 0 "" brown 8 2 2 0 +simundump kchan /kinetics/ActIP3R/chan 0 8 0.1 0 1 0 "" brown 8 2 2 0 simundump text /kinetics/ActIP3R/chan/notes 0 "" call /kinetics/ActIP3R/chan/notes LOAD \ "" @@ -207,7 +207,7 @@ simundump kpool /kinetics/DEND_ER/leakPool 0 0 1 1 234 234 0 0 234 0 \ simundump text /kinetics/DEND_ER/leakPool/notes 0 "" call /kinetics/DEND_ER/leakPool/notes LOAD \ "" -simundump kchan /kinetics/DEND_ER/leakPool/leakChan 0 40 0.1 0 1 0 "" brown \ +simundump kchan /kinetics/DEND_ER/leakPool/leakChan 0 0.04 0.1 0 1 0 "" brown \ 45 4 5 0 simundump text /kinetics/DEND_ER/leakPool/leakChan/notes 0 "" call /kinetics/DEND_ER/leakPool/leakChan/notes LOAD \ diff --git a/moose-examples/tutorials/Rdesigneur/chem/CICRwithConcChan.g b/moose-examples/tutorials/Rdesigneur/chem/CICRwithConcChan.g index a8c3792c4a7ade7df37e79227c892882a157dfdb..136de8251370233e84312632024de240fcbd4769 100644 --- a/moose-examples/tutorials/Rdesigneur/chem/CICRwithConcChan.g +++ b/moose-examples/tutorials/Rdesigneur/chem/CICRwithConcChan.g @@ -1,7 +1,7 @@ //genesis // kkit Version 11 flat dumpfile -// Saved on Fri Aug 17 04:02:55 2018 +// Saved on Fri Mar 29 19:34:16 2019 include kkit {argv 1} @@ -71,16 +71,6 @@ simundump kpool /kinetics/CaCyt 0 1e-13 0.079999 0.079999 149.35 149.35 0 0 \ simundump text /kinetics/CaCyt/notes 0 "" call /kinetics/CaCyt/notes LOAD \ "" -simundump kpool /kinetics/phase 0 0.0 100 100 1.8669e+05 1.8669e+05 0 0 \ - 1866.9 0 /kinetics/geometry 49 black 9 9 0 -simundump text /kinetics/phase/notes 0 "" -call /kinetics/phase/notes LOAD \ -"" -simundump kpool /kinetics/ampl 0 0.0 0 0 0 0 0 0 1866.9 0 /kinetics/geometry \ - 63 black 10 5 0 -simundump text /kinetics/ampl/notes 0 "" -call /kinetics/ampl/notes LOAD \ -"" simundump kpool /kinetics/CaIP3_3_R 0 0.0 0 0 0 0 0 0 1866.9 0 \ /kinetics/geometry 20 black 4 7 0 simundump text /kinetics/CaIP3_3_R/notes 0 "" @@ -146,11 +136,6 @@ simundump kpool /kinetics/Ca2_IP3_3_R 0 0.0 0 0 0 0 0 0 1866.9 0 \ simundump text /kinetics/Ca2_IP3_3_R/notes 0 "" call /kinetics/Ca2_IP3_3_R/notes LOAD \ "" -simundump kpool /kinetics/CaStim 0 0.0 0 0 0 0 0 0 1866.9 4 \ - /kinetics/geometry 61 black 7 5 0 -simundump text /kinetics/CaStim/notes 0 "" -call /kinetics/CaStim/notes LOAD \ -"" simundump kpool /kinetics/Mirror_CaIP3_3_R 0 0.0 0 0 0 0 0 0 1866.9 0 \ /kinetics/geometry 4 black -1 4 0 simundump text /kinetics/Mirror_CaIP3_3_R/notes 0 "" @@ -161,20 +146,10 @@ simundump kpool /kinetics/ActIP3R 0 0.0 0 0 0 0 0 0 1866.9 0 \ simundump text /kinetics/ActIP3R/notes 0 "" call /kinetics/ActIP3R/notes LOAD \ "" -simundump kchan /kinetics/ActIP3R/chan 0 12720 0.1 0 1 0 "" brown 8 2 2 0 +simundump kchan /kinetics/ActIP3R/chan 0 8 0.1 0 1 0 "" brown 8 2 2 0 simundump text /kinetics/ActIP3R/chan/notes 0 "" call /kinetics/ActIP3R/chan/notes LOAD \ "" -simundump kpool /kinetics/basal 0 0.0 0 0 0 0 0 0 1866.9 0 /kinetics/geometry \ - 8 black 2 -1 0 -simundump text /kinetics/basal/notes 0 "" -call /kinetics/basal/notes LOAD \ -"" -simundump kpool /kinetics/BufPool 0 0.0 0 0 0 0 0 0 1866.9 4 \ - /kinetics/geometry 26 black 1 9 0 -simundump text /kinetics/BufPool/notes 0 "" -call /kinetics/BufPool/notes LOAD \ -"" simundump kreac /kinetics/CaMreac1 0 0.0045449 8.4853 "" white black 5 0 0 simundump text /kinetics/CaMreac1/notes 0 "" call /kinetics/CaMreac1/notes LOAD \ @@ -212,7 +187,7 @@ simundump kpool /kinetics/leakPool 0 0 1 1 234 234 0 0 234 0 \ simundump text /kinetics/leakPool/notes 0 "" call /kinetics/leakPool/notes LOAD \ "" -simundump kchan /kinetics/leakPool/leakChan 0 15.3 0.1 0 1 0 "" brown 45 4 5 \ +simundump kchan /kinetics/leakPool/leakChan 0 0.04 0.1 0 1 0 "" brown 45 4 5 \ 0 simundump text /kinetics/leakPool/leakChan/notes 0 "" call /kinetics/leakPool/leakChan/notes LOAD \ @@ -229,7 +204,7 @@ simundump xplot /graphs/conc2/CaER.Co 3 524288 \ "delete_plot.w <s> <d>; edit_plot.D <w>" 0 0 0 1 simundump xgraph /moregraphs/conc3 0 0 100 0 1 0 simundump xgraph /moregraphs/conc4 0 0 100 0 1 0 -simundump xcoredraw /edit/draw 0 -3 12 -3 11 +simundump xcoredraw /edit/draw 0 -1.5666 11.334 -1.8008 10.24 simundump xtree /edit/draw/tree 0 \ /kinetics/#[],/kinetics/#[]/#[],/kinetics/#[]/#[]/#[][TYPE!=proto],/kinetics/#[]/#[]/#[][TYPE!=linkinfo]/##[] \ "edit_elm.D <v>; drag_from_edit.w <d> <S> <x> <y> <z>" auto 0.6 diff --git a/moose-examples/tutorials/Rdesigneur/ex7.2_CICR.py b/moose-examples/tutorials/Rdesigneur/ex7.2_CICR.py index 38447a8d0a2c040874cc6c8f1b91e88481a1c1fb..a9725b6faf0b539726f7a5f1df52c814c29183c3 100644 --- a/moose-examples/tutorials/Rdesigneur/ex7.2_CICR.py +++ b/moose-examples/tutorials/Rdesigneur/ex7.2_CICR.py @@ -37,6 +37,6 @@ IP3 = moose.element( '/model/chem/dend/IP3' ) IP3.vec.concInit = 0.004 IP3.vec[0].concInit = 0.02 moose.reinit() -moose.start( 40 ) +moose.start( 20 ) rdes.display() diff --git a/moose-examples/tutorials/Rdesigneur/ex7.6_func_controls_reac_rate.py b/moose-examples/tutorials/Rdesigneur/ex7.6_func_controls_reac_rate.py new file mode 100644 index 0000000000000000000000000000000000000000..b8c8fb4c64b3f80d9014633108baba45a55eaf1d --- /dev/null +++ b/moose-examples/tutorials/Rdesigneur/ex7.6_func_controls_reac_rate.py @@ -0,0 +1,67 @@ +######################################################################## +# This example illustrates how a function can be used to control a reaction +# rate. This kind of calculation is appropriate when we need to link +# different kinds of physical processses with chemical reactions, for +# example, membrane curvature with molecule accumulation. The use of +# functions to modify reaction rates should be avoided in purely chemical +# systems since they obscure the underlying chemistry, and do not map +# cleanly to stochastic calculations. +# +# In this example we simply have a molecule C that controls the forward +# rate of a reaction that converts A to B. C is a function of location +# on the cylinder, and is fixed. In more elaborate computations we could +# have a function of multiple molecules, some of which could be changing and +# others could be buffered. +# +# Copyright (C) Upinder S. Bhalla NCBS 2018 +# Released under the terms of the GNU Public License V3. +######################################################################## + +import numpy as np +import moose +import pylab +import rdesigneur as rd + + +def makeFuncRate(): + model = moose.Neutral( '/library' ) + model = moose.Neutral( '/library/chem' ) + compt = moose.CubeMesh( '/library/chem/compt' ) + compt.volume = 1e-15 + A = moose.Pool( '/library/chem/compt/A' ) + B = moose.Pool( '/library/chem/compt/B' ) + C = moose.Pool( '/library/chem/compt/C' ) + reac = moose.Reac( '/library/chem/compt/reac' ) + func = moose.Function( '/library/chem/compt/reac/func' ) + func.x.num = 1 + func.expr = "(x0/1e8)^2" + moose.connect( C, 'nOut', func.x[0], 'input' ) + moose.connect( func, 'valueOut', reac, 'setNumKf' ) + moose.connect( reac, 'sub', A, 'reac' ) + moose.connect( reac, 'prd', B, 'reac' ) + + A.concInit = 1 + B.concInit = 0 + C.concInit = 0 + reac.Kb = 1 + + +makeFuncRate() + +rdes = rd.rdesigneur( + turnOffElec = True, + #This subdivides the 50-micron cylinder into 2 micron voxels + diffusionLength = 2e-6, + cellProto = [['somaProto', 'soma', 5e-6, 50e-6]], + chemProto = [['chem', 'chem']], + chemDistrib = [['chem', 'soma', 'install', '1' ]], + plotList = [['soma', '1', 'dend/A', 'conc', 'A conc', 'wave'], + ['soma', '1', 'dend/C', 'conc', 'C conc', 'wave']], +) +rdes.buildModel() + +C = moose.element( '/model/chem/dend/C' ) +C.vec.concInit = [ 1+np.sin(x/5.0) for x in range( len(C.vec) ) ] +moose.reinit() +moose.start(10) +rdes.display() diff --git a/moose-gui/mgui.py b/moose-gui/mgui.py index d5a900abe4b3df5e4b6e3ffccfbc847ce342c84f..0ecb96fe2ca97b62136fd0cebccb8a15470c41b2 100644 --- a/moose-gui/mgui.py +++ b/moose-gui/mgui.py @@ -6,7 +6,7 @@ # Maintainer: HarshaRani # Created: Mon Nov 12 09:38:09 2012 (+0530) # Version: -# Last-Updated: Fri Sep 7 14:54:33 2017 (+0530) +# Last-Updated: Fri Sep 20 00:54:33 2018 (+0530) # By: Harsha # Update #: # URL: @@ -45,13 +45,18 @@ '''' 2018 -Sep 7: popup is closed if exist +Sep 20 : Lot of duplicate code removed + Function call made when filename or filepath is passed in command line +Sep 19 : From the cmd line if a directory is passed, then Gui opens up the dialog file for the folder, + window is resized to maximum width, clean warning message if filename or path is wrong + Added model info QmessageBox +Sep 7 : popup is closed if exist 2017 -Aug 31: Pass file from the command to load into gui - : added dsolver in disableModel function is used to unset the solver for the model - into moose-gui which are not to be run. +Aug 31 : Pass file from the command to load into gui + : added dsolver in disableModel function is used to unset the solver for the model + into moose-gui which are not to be run. -Oct 5: clean up with round trip of dialog_exe +Oct 5 : clean up with round trip of dialog_exe ''' # Code: @@ -196,6 +201,7 @@ class MWindow(QtGui.QMainWindow): pass else: cmdfilepath = os.path.abspath(sys.argv[1]) + try: sys.argv[2] except: @@ -207,6 +213,7 @@ class MWindow(QtGui.QMainWindow): filepath,fileName = os.path.split(cmdfilepath) modelRoot,extension = os.path.splitext(fileName) if extension == '.py': + self.setWindowState(QtCore.Qt.WindowMaximized) self.show() self.createPopup() freeCursor() @@ -215,16 +222,23 @@ class MWindow(QtGui.QMainWindow): QtGui.QApplication.restoreOverrideCursor() return if not os.path.exists(cmdfilepath): + self.setWindowState(QtCore.Qt.WindowMaximized) self.show() self.createPopup() - reply = QtGui.QMessageBox.information(self,"Model file can not open","Check filename or filepath ",QtGui.QMessageBox.Ok) + reply = QtGui.QMessageBox.information(self,"Model file can not open","File Not Found \n \nCheck filename or filepath\n ",QtGui.QMessageBox.Ok) if reply == QtGui.QMessageBox.Ok: QtGui.QApplication.restoreOverrideCursor() return + if os.path.isdir(cmdfilepath): + self.setWindowState(QtCore.Qt.WindowMaximized) + self.show() + self.loadModelDialogFunc(cmdfilepath) + else: filePath = filepath+'/'+fileName ret = loadFile(str(filePath), '%s' % (modelRoot), solver, merge=False) - self.objectEditSlot('/',False) + #self.objectEditSlot('/',False) + self.objectEditSlot(ret['model'].path,False) pluginLookup = '%s/%s' % (ret['modeltype'], ret['subtype']) try: pluginName = subtype_plugin_map['%s/%s' % (ret['modeltype'], ret['subtype'])] @@ -249,26 +263,10 @@ class MWindow(QtGui.QMainWindow): modelAnno.dirpath = str(filepath) self.setPlugin(pluginName, ret['model'].path) + self.setWindowState(QtCore.Qt.WindowMaximized) self.show() - # if pluginName == 'kkit': - # QtCore.QCoreApplication.sendEvent(self.plugin.getEditorView().getCentralWidget().view, QtGui.QKeyEvent(QtCore.QEvent.KeyPress, Qt.Qt.Key_A, Qt.Qt.NoModifier)) - - # noOfCompt = len(moose.wildcardFind(ret['model'].path+'/##[ISA=ChemCompt]')) - # grp = 0 - # for c in moose.wildcardFind(ret['model'].path+'/##[ISA=ChemCompt]'): - # noOfGrp = moose.wildcardFind(moose.element(c).path+'/#[TYPE=Neutral]') - # grp = grp+len(noOfGrp) - - # noOfPool = len(moose.wildcardFind(ret['model'].path+'/##[ISA=PoolBase]')) - # noOfFunc = len(moose.wildcardFind(ret['model'].path+'/##[ISA=Function]')) - # noOfReac = len(moose.wildcardFind(ret['model'].path+'/##[ISA=ReacBase]')) - # noOfEnz = len(moose.wildcardFind(ret['model'].path+'/##[ISA=EnzBase]')) - # noOfStimtab = len(moose.wildcardFind(ret['model'].path+'/##[ISA=StimulusTable]')) - - # reply = QtGui.QMessageBox.information(self,"Model Info","Model has : \n %s Compartment \t \n %s Group \t \n %s Pool \t \n %s Function \t \n %s reaction \t \n %s Enzyme \t \n %s StimulusTable" %(noOfCompt, grp, noOfPool, noOfFunc, noOfReac, noOfEnz, noOfStimtab)) - # if reply == QtGui.QMessageBox.Ok: - # QtGui.QApplication.restoreOverrideCursor() - # return + if pluginName == 'kkit': + self.displaymodelInfo(ret) else: self.createPopup() @@ -571,7 +569,6 @@ class MWindow(QtGui.QMainWindow): if name == 'kkit': self.objectEditDockWidget.objectNameChanged.connect(self.plugin.getEditorView().getCentralWidget().updateItemSlot) self.objectEditDockWidget.colorChanged.connect(self.plugin.getEditorView().getCentralWidget().updateColorSlot) - self.setCurrentView('editor') freeCursor() return self.plugin @@ -646,7 +643,8 @@ class MWindow(QtGui.QMainWindow): self.plugin.setCurrentView(view) if view =='run': #Harsha: This will clear out object editor's objectpath and make it invisible - self.objectEditSlot('/',False) + #self.objectEditSlot('/',False) + self.objectEditDockWidget.setVisible(False) targetView = None newSubWindow = True @@ -1222,6 +1220,15 @@ class MWindow(QtGui.QMainWindow): for table in moose.wildcardFind( modelPath+'/data/graph#/#' ): table.tick = -1 + def loadModelDialogFunc(self,directorypassed): + """ This is from command line the filepath and file name is passed + """ + dialog = LoaderDialog(self, + self.tr('Load model from file'),directorypassed) + + if dialog.exec_(): + self.passtoPluginCheck(dialog) + def loadModelDialogSlot(self): """Start a file dialog to choose a model file. @@ -1245,66 +1252,48 @@ class MWindow(QtGui.QMainWindow): activeWindow = None # This to be used later to refresh the current widget with newly loaded model dialog = LoaderDialog(self, self.tr('Load model from file')) - if dialog.exec_(): - valid = False - ret = [] - ret,pluginName = self.checkPlugin(dialog) + self.passtoPluginCheck(dialog) + + def passtoPluginCheck(self, dialog): + valid = False + ret = [] + ret,pluginName = self.checkPlugin(dialog) + valid,ret = self.dialog_check(ret) + + if valid == True: + modelAnno = moose.Annotator(ret['model'].path+'/info') + if ret['subtype']: + modelAnno.modeltype = ret['subtype'] + else: + modelAnno.modeltype = ret['modeltype'] + modelAnno.dirpath = str(dialog.directory().absolutePath()) + self.loadedModelsAction(ret['model'].path,pluginName) + self.setPlugin(pluginName, ret['model'].path) + if pluginName == 'kkit': - if (ret['subtype'] == 'sbml' and ret['foundlib'] == False): - reply = QtGui.QMessageBox.question(self, "python-libsbml is not found.","\n Read SBML is not possible.\n This can be installed using \n \n pip python-libsbml or \n apt-get install python-libsbml", - QtGui.QMessageBox.Ok) - if reply == QtGui.QMessageBox.Ok: - QtGui.QApplication.restoreOverrideCursor() - return - else: - if ret['loaderror'] != "": - reply = QtGui.QMessageBox.question(self, "Model can't be loaded", ret['loaderror']+" \n \n Do you want another file", - QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) - if reply == QtGui.QMessageBox.Yes: - dialog = LoaderDialog(self,self.tr('Load model from file')) - if dialog.exec_(): - valid = False - ret = [] - pluginName = None - ret,pluginName = self.checkPlugin(dialog) - - valid, ret = self.dialog_check(ret) - else: - QtGui.QApplication.restoreOverrideCursor() - return valid - else: + self.displaymodelInfo(ret) + + def displaymodelInfo(self,ret): + QtCore.QCoreApplication.sendEvent(self.plugin.getEditorView().getCentralWidget().view, QtGui.QKeyEvent(QtCore.QEvent.KeyPress, Qt.Qt.Key_A, Qt.Qt.NoModifier)) + + noOfCompt = len(moose.wildcardFind(ret['model'].path+'/##[ISA=ChemCompt]')) + grp = 0 + for c in moose.wildcardFind(ret['model'].path+'/##[ISA=ChemCompt]'): + noOfGrp = moose.wildcardFind(moose.element(c).path+'/#[TYPE=Neutral]') + grp = grp+len(noOfGrp) + + noOfPool = len(moose.wildcardFind(ret['model'].path+'/##[ISA=PoolBase]')) + noOfFunc = len(moose.wildcardFind(ret['model'].path+'/##[ISA=Function]')) + noOfReac = len(moose.wildcardFind(ret['model'].path+'/##[ISA=ReacBase]')) + noOfEnz = len(moose.wildcardFind(ret['model'].path+'/##[ISA=EnzBase]')) + noOfStimtab = len(moose.wildcardFind(ret['model'].path+'/##[ISA=StimulusTable]')) + + reply = QtGui.QMessageBox.information(self,"Model Info","Model has : \n %s Compartment \t \n %s Group \t \n %s Pool \t \n %s Function \t \n %s reaction \t \n %s Enzyme \t \n %s StimulusTable" %(noOfCompt, grp, noOfPool, noOfFunc, noOfReac, noOfEnz, noOfStimtab)) + if reply == QtGui.QMessageBox.Ok: + QtGui.QApplication.restoreOverrideCursor() + return - valid = True - if valid == True: - modelAnno = moose.Annotator(ret['model'].path+'/info') - if ret['subtype']: - modelAnno.modeltype = ret['subtype'] - else: - modelAnno.modeltype = ret['modeltype'] - modelAnno.dirpath = str(dialog.directory().absolutePath()) - self.loadedModelsAction(ret['model'].path,pluginName) - self.setPlugin(pluginName, ret['model'].path) - if pluginName == 'kkit': - QtCore.QCoreApplication.sendEvent(self.plugin.getEditorView().getCentralWidget().view, QtGui.QKeyEvent(QtCore.QEvent.KeyPress, Qt.Qt.Key_A, Qt.Qt.NoModifier)) - - noOfCompt = len(moose.wildcardFind(ret['model'].path+'/##[ISA=ChemCompt]')) - grp = 0 - for c in moose.wildcardFind(ret['model'].path+'/##[ISA=ChemCompt]'): - noOfGrp = moose.wildcardFind(moose.element(c).path+'/#[TYPE=Neutral]') - grp = grp+len(noOfGrp) - - noOfPool = len(moose.wildcardFind(ret['model'].path+'/##[ISA=PoolBase]')) - noOfFunc = len(moose.wildcardFind(ret['model'].path+'/##[ISA=Function]')) - noOfReac = len(moose.wildcardFind(ret['model'].path+'/##[ISA=ReacBase]')) - noOfEnz = len(moose.wildcardFind(ret['model'].path+'/##[ISA=EnzBase]')) - noOfStimtab = len(moose.wildcardFind(ret['model'].path+'/##[ISA=StimulusTable]')) - - reply = QtGui.QMessageBox.information(self,"Model Info","Model has : \n %s Compartment \t \n %s Group \t \n %s Pool \t \n %s Function \t \n %s reaction \t \n %s Enzyme \t \n %s StimulusTable" %(noOfCompt, grp, noOfPool, noOfFunc, noOfReac, noOfEnz, noOfStimtab)) - if reply == QtGui.QMessageBox.Ok: - QtGui.QApplication.restoreOverrideCursor() - return - def checkPlugin(self,dialog): fileNames = dialog.selectedFiles() for fileName in fileNames: @@ -1360,7 +1349,7 @@ class MWindow(QtGui.QMainWindow): def newModelDialogSlot(self): #Harsha: Create a new dialog widget for model building - if popup: + if self.popup: self.popup.close() newModelDialog = DialogWidget() if newModelDialog.exec_(): diff --git a/moose-gui/mload.py b/moose-gui/mload.py index 9fc753e64551b388f7b605d0fe326fc75738e930..3b9783282db59afed47d012d15d0069f2bf83d75 100644 --- a/moose-gui/mload.py +++ b/moose-gui/mload.py @@ -61,7 +61,8 @@ from PyQt4 import QtGui, QtCore, Qt from plugins.setsolver import * from moose.SBML import * from plugins.kkitOrdinateUtil import * - +import moose._moose as moose +import moose.utils as mutils def loadGenCsp(target,filename,solver="gsl"): target = target.replace(" ", "") path = '/'+target @@ -72,7 +73,7 @@ def loadGenCsp(target,filename,solver="gsl"): moose.delete(mpath) modelpath1 = moose.Neutral('%s' %(target)) modelpath = moose.Neutral('%s/%s' %(modelpath1.path,"model")) - model = moose.loadModel(filename, modelpath.path,solver) + model = mutils.loadModel(filename, modelpath.path,solver) if not moose.exists(modelpath1.path+'/data'): graphspath = moose.Neutral('%s/%s' %(modelpath1.path,"data")) @@ -189,7 +190,7 @@ def loadFile(filename, target, solver="gsl", merge=True): moose.Annotator(moose.element(modelpath).path+'/info').modeltype = "kkit" else: print (" path doesn't exists") - moose.le(modelpath) + #moose.le(modelpath) else: print ('Only kkit and prototype files can be loaded.') diff --git a/moose-gui/objectedit.py b/moose-gui/objectedit.py index d5d66b60c96fa506af50e620260a109f5deb8e29..11a35b66948262d60c12cdd528ab421e7b5aa873 100644 --- a/moose-gui/objectedit.py +++ b/moose-gui/objectedit.py @@ -6,7 +6,7 @@ # Maintainer: # Created: Wed Jun 30 11:18:34 2010 (+0530) # Version: -# Last-Updated: Mon Sep 10 11:05:59 2017 (+0530) +# Last-Updated: Fri Feb 01 11:05:59 2017 (+0530) # By: Harsha # Update #: # URL: @@ -77,11 +77,13 @@ import sys from collections import deque import traceback -sys.path.append('../python') +#sys.path.append('../python') import moose import defaults import config -from plugins.kkitUtil import getColor +#from plugins.kkitUtil import getColor +from moose.chemUtil.chemConnectUtil import getColor + #these fields will be ignored extra_fields = ['this', 'me', @@ -437,7 +439,7 @@ class ObjectEditView(QtGui.QTableView): def setColor(self, color): self.colorButton.setStyleSheet( "QPushButton {" - + "background-color: {0}; color: {0};".format(color.name()) + + "background-color: {0}; color: {0};".format(color) + "}" ) self.colorDialog.setCurrentColor(color) diff --git a/moose-gui/plugins/kkit.py b/moose-gui/plugins/kkit.py index 3cd531bb4498af9fc86fc75bc11a9a4376f602d0..c375e06905103bff17f50aaeaea810589327399b 100644 --- a/moose-gui/plugins/kkit.py +++ b/moose-gui/plugins/kkit.py @@ -6,11 +6,17 @@ __version__ = "1.0.0" __maintainer__ = "HarshaRani" __email__ = "hrani@ncbs.res.in" __status__ = "Development" -__updated__ = "Sep 11 2018" +__updated__ = "Feb 22 2019" #Change log: +#2019 +#Feb 22: cross compartment molecules are checked for destination # 2018 -#sep 11: comparment size is calculated based on group sceneBoundingRect size +#Oct 26: xfer cross compartment molecules are hidden and for cross compartment reaction's +# connection are now dotted line +#Oct 10: filedialog default is sbml +# layout co-ordainates are updated with scenepos +#Sep 11: comparment size is calculated based on group sceneBoundingRect size #Sep 07: in positionChange all the group's boundingRect is calculated # and when group is moved the children's position are stored #Jun 18: update the color of the group from objecteditor @@ -38,6 +44,7 @@ import RunWidget from os.path import expanduser #from setsolver import * from moose.chemUtil.add_Delete_ChemicalSolver import * +import re class KkitPlugin(MoosePlugin): """Default plugin for MOOSE GUI""" @@ -62,41 +69,25 @@ class KkitPlugin(MoosePlugin): self.getEditorView() def SaveModelDialogSlot(self): - type_sbml = 'SBML' - type_genesis = 'Genesis' + dirpath = "" - # if moose.Annotator(self.modelRoot+'/model/info'): - # moose.Annotator(self.modelRoot+'/model/info') - # mooseAnno = moose.Annotator(self.modelRoot+'/model/info') - #dirpath = mooseAnno.dirpath if not dirpath: dirpath = expanduser("~") - filters = {'SBML(*.xml)': type_sbml,'Genesis(*.g)':type_genesis} - - filename,filter_ = QtGui.QFileDialog.getSaveFileNameAndFilter(None,'Save File',dirpath,';;'.join(filters)) + filters = {'SBML(*.xml)': 'SBML','Genesis(*.g)':'Genesis'} + #filename,filter_ = QtGui.QFileDialog.getSaveFileNameAndFilter(None,'Save File',dirpath,';;'.join(filters)) + filename,filter_ = QtGui.QFileDialog.getSaveFileNameAndFilter(None,'Save File',dirpath,"SBML(*.xml);;Genesis(*.g)") extension = "" if str(filename).rfind('.') != -1: filename = filename[:str(filename).rfind('.')] if str(filter_).rfind('.') != -1: extension = filter_[str(filter_).rfind('.'):len(filter_)-1] + if filename: filename = filename if filters[str(filter_)] == 'SBML': - self.sceneObj = KkitEditorView(self).getCentralWidget().mooseId_GObj self.coOrdinates = {} self.plugin = KkitEditorView(self).getCentralWidget().plugin - self.defaultScenewidth = KkitEditorView(self).getCentralWidget().defaultScenewidth - self.defaultSceneheight = KkitEditorView(self).getCentralWidget().defaultSceneheight self.coOrdinates = KkitEditorView(self).getCentralWidget().getsceneCord() - ''' - for k,v in self.sceneObj.items(): - if moose.exists(moose.element(k).path+'/info'): - annoInfo = Annotator(k.path+'/info') - if moose.element(self.plugin.modelRoot+'/info').modeltype == 'kkit': - self.coOrdinates[k] = {'x':annoInfo.x*self.defaultScenewidth, 'y':annoInfo.y*self.defaultSceneheight} - else: - self.coOrdinates[k] = {'x':annoInfo.x, 'y':annoInfo.y} - ''' #writeerror = moose.writeSBML(self.modelRoot,str(filename),self.coOrdinates) writeerror = -2 conisitencyMessages = "" @@ -344,8 +335,8 @@ class KineticsWidget(EditorWidgetBase): self.arrowsize = 2 self.reset() - self.defaultSceneheight = 800#1000 - self.defaultScenewidth = 1000#2400 + self.defaultSceneheight = 1#800#1000 + self.defaultScenewidth = 1#000#2400 self.positionInfoExist = True self.defaultComptsize = 5 self.srcdesConnection = {} @@ -376,13 +367,10 @@ class KineticsWidget(EditorWidgetBase): def getsceneCord(self): self.cord = {} + self.view.setRefWidget("runView") for item in self.sceneContainer.items(): if isinstance(item,KineticsDisplayItem): - #item.refresh(scale) - #self.update() - xpos = item.scenePos().x() - ypos = item.scenePos().y() - self.cord[item.mobj] = {'x':xpos,'y':ypos} + self.cord[item.mobj] = {'x':item.scenePos().x(),'y':item.scenePos().y()} return self.cord def updateModelView(self): @@ -459,18 +447,15 @@ class KineticsWidget(EditorWidgetBase): self.srcdesConnection = {} setupItem(self.modelRoot,self.srcdesConnection) - - # if not self.positionInfoExist: - # autoCoordinates(self.meshEntry,self.srcdesConnection) + #self.noPositionInfo = False if not self.noPositionInfo: self.autocoordinates = True #self.xmin,self.xmax,self.ymin,self.ymax,self.autoCordinatepos = autoCoordinates(self.meshEntry,self.srcdesConnection) - #print " after ",self.xmin,self.xmax, self.ymin, self.ymax,self.autoCordinatepos autoCoordinates(self.meshEntry,self.srcdesConnection) self.size = QtCore.QSize(1000 ,550) - ''' + ''' if self.xmax-self.xmin != 0: self.xratio = (self.size.width()-10)/(self.xmax-self.xmin) else: self.xratio = self.size.width()-10 @@ -485,7 +470,7 @@ class KineticsWidget(EditorWidgetBase): self.xratio = 1 if self.yratio == 0: self.yratio = 1 - ''' + ''' def sizeHint(self): return QtCore.QSize(800,400) @@ -503,6 +488,7 @@ class KineticsWidget(EditorWidgetBase): self.positionChange(mooseObject) self.view.removeConnector() self.view.showConnector(item) + def updateColorSlot(self,mooseObject, colour): #Color slot for changing background color for Pool,Enz and group from objecteditor anninfo = moose.Annotator(mooseObject.path+'/info') @@ -631,6 +617,7 @@ class KineticsWidget(EditorWidgetBase): elif isinstance(moose.element(v), moose.Neutral): group_parent = self.qGraGrp[v] self.createGroup(k,group_parent) + for cmpt_grp,memb in self.meshEntry.items(): if len(memb): if isinstance(moose.element(cmpt_grp),moose.ChemCompt): @@ -643,6 +630,7 @@ class KineticsWidget(EditorWidgetBase): self.groupChildrenBoundingRect() # compartment's rectangle size is calculated depending on children self.comptChildrenBoundingRect() + def mObjontoscene(self,memb,mclass,qtGrpparent): try: value = memb[mclass] @@ -651,7 +639,6 @@ class KineticsWidget(EditorWidgetBase): else: for mObj in memb[mclass]: minfo = mObj.path+'/info' - if mObj.className in['Enz',"ZombieEnz"]: mItem = EnzItem(mObj,qtGrpparent) @@ -669,18 +656,20 @@ class KineticsWidget(EditorWidgetBase): minfo = (mObj.parent).path+'/info' mItem = CplxItem(mObj,self.mooseId_GObj[element(mObj).parent]) self.mooseId_GObj[element(mObj.getId())] = mItem + elif mclass == "function": if isinstance(moose.element(mObj.parent),moose.PoolBase): minfo = moose.element(mObj).path+'/info' Af = Annotator(minfo) qtGrpparent = self.mooseId_GObj[element(mObj.parent)] mItem = FuncItem(mObj,qtGrpparent) + elif mclass == "stimTab": minfo = mObj.path+'/info' mItem = TableItem(mObj,qtGrpparent) self.mooseId_GObj[element(mObj.getId())] = mItem self.setupDisplay(minfo,mItem,mclass) - + def createGroup(self,key,parent): self.new_GRP = GRPItem(parent,0,0,0,0,key) self.qGraGrp[key] = self.new_GRP @@ -696,12 +685,13 @@ class KineticsWidget(EditorWidgetBase): v.setPen(QtGui.QPen(Qt.QColor(grpcolor), self.comptPen, Qt.Qt.SolidLine, Qt.Qt.RoundCap, Qt.Qt.RoundJoin)) def comptChildrenBoundingRect(self): + comptlist = [] for k, v in self.qGraCompt.items(): # compartment's rectangle size is calculated depending on children rectcompt = calculateChildBoundingRect(v) v.setRect(rectcompt.x()-10,rectcompt.y()-10,(rectcompt.width()+20),(rectcompt.height()+20)) v.setPen(QtGui.QPen(Qt.QColor(66,66,66,100), self.comptPen, Qt.Qt.SolidLine, Qt.Qt.RoundCap, Qt.Qt.RoundJoin)) - + def createCompt(self,key): self.new_Compt = ComptItem(self,0,0,0,0,key) self.qGraCompt[key] = self.new_Compt @@ -738,34 +728,34 @@ class KineticsWidget(EditorWidgetBase): else: xpos,ypos = self.positioninfo(info) - self.xylist = [xpos,ypos] - self.xyCord[moose.element(info).parent] = [xpos,ypos] - elif isinstance(self,kineticRunWidget): self.editormooseId_GObj = self.editor.getCentralWidget().mooseId_GObj editorItem = self.editormooseId_GObj[moose.element(info).parent] xpos = editorItem.scenePos().x() - ypos = editorItem.scenePos().y() - #Annoinfo.x = xpos - #Annoinfo.y = -ypos + ypos = editorItem.scenePos().y() graphicalObj.setDisplayProperties(xpos,ypos,textcolor,bgcolor) - #Annoinfo.x = xpos - #Annoinfo.y = ypos + def positioninfo(self,iteminfo): '''By this time, model loaded from kkit,cspace,SBML would have info field created and co-ordinates are added either by autocoordinates (for cspace,SBML(unless it is not saved from moose)) or from kkit ''' - if moose.Annotator(self.plugin.modelRoot+'/info').modeltype == 'kkit': - x = self.defaultScenewidth * float(element(iteminfo).getField('x')) - y = self.defaultSceneheight * float(element(iteminfo).getField('y')) - #x = x /self.defaultScenewidth - #y = y /self.defaultSceneheight - else: - x = float(element(iteminfo).getField('x')) - y = float(element(iteminfo).getField('y')) - self.defaultScenewidth = 1 - self.defaultSceneheight = 1 + + x = float(element(iteminfo).getField('x')) + y = float(element(iteminfo).getField('y')) + #print " positioninfo", iteminfo,x,y + # if moose.Annotator(self.plugin.modelRoot+'/info').modeltype == 'kkit': + # x = self.defaultScenewidth * float(element(iteminfo).getField('x')) + # y = self.defaultSceneheight * float(element(iteminfo).getField('y')) + # print " positioninfo ",iteminfo, element(iteminfo).getField('x'), element(iteminfo).getField('y'), x, y + + # #x = x /self.defaultScenewidth + # #y = y /self.defaultSceneheight + # else: + # x = float(element(iteminfo).getField('x')) + # y = float(element(iteminfo).getField('y')) + # self.defaultScenewidth = 1 + # self.defaultSceneheight = 1 return(x,y) def drawLine_arrow(self, itemignoreZooming=False): @@ -776,20 +766,39 @@ class KineticsWidget(EditorWidgetBase): # key is Function and value is [list of pool] (list) #src = self.mooseId_GObj[inn] + linetype = "regular" if isinstance(out,tuple): src = self.mooseId_GObj[inn] if len(out[0])== 0: print (inn.className + ' : ' +inn.name+ " doesn't output message") else: for items in (items for items in out[0] ): + if re.search("xfer",element(items[0]).name): + xrefPool = items[0].name[:items[0].name.index("_xfer_")] + xrefCompt = items[0].name[items[0].name.index("_xfer_") + len("_xfer_"):] + orgCompt = moose.wildcardFind(self.modelRoot+'/##[FIELD(name)='+xrefCompt+']')[0] + orgPool = moose.wildcardFind(orgCompt.path+'/##[FIELD(name)='+xrefPool+']')[0] + itemslist = list(items) + itemslist[0] = orgPool + items = tuple(itemslist) + linetype = "crosscompt" des = self.mooseId_GObj[element(items[0])] - self.lineCord(src,des,items,itemignoreZooming) + self.lineCord(src,des,items,itemignoreZooming,linetype) if len(out[1]) == 0: print (inn.className + ' : ' +inn.name+ " doesn't output message") else: for items in (items for items in out[1] ): + if re.search("xfer",element(items[0]).name): + xrefPool = items[0].name[:items[0].name.index("_xfer_")] + xrefCompt = items[0].name[items[0].name.index("_xfer_") + len("_xfer_"):] + orgCompt = moose.wildcardFind(self.modelRoot+'/##[FIELD(name)='+xrefCompt+']')[0] + orgPool = moose.wildcardFind(orgCompt.path+'/##[FIELD(name)='+xrefPool+']')[0] + itemslist = list(items) + itemslist[0] = orgPool + items = tuple(itemslist) + linetype = "crosscompt" des = self.mooseId_GObj[element(items[0])] - self.lineCord(src,des,items,itemignoreZooming) + self.lineCord(src,des,items,itemignoreZooming,linetype) elif isinstance(out,list): if len(out) == 0: if inn.className == "StimulusTable": @@ -799,9 +808,19 @@ class KineticsWidget(EditorWidgetBase): else: src = self.mooseId_GObj[inn] for items in (items for items in out ): + if re.search("xfer",element(items[0]).name): + xrefPool = items[0].name[:items[0].name.index("_xfer_")] + xrefCompt = items[0].name[items[0].name.index("_xfer_") + len("_xfer_"):] + orgCompt = moose.wildcardFind(self.modelRoot+'/##[FIELD(name)='+xrefCompt+']')[0] + orgPool = moose.wildcardFind(orgCompt.path+'/##[FIELD(name)='+xrefPool+']')[0] + itemslist = list(items) + itemslist[0] = orgPool + items = tuple(itemslist) + linetype = "crosscompt" des = self.mooseId_GObj[element(items[0])] - self.lineCord(src,des,items,itemignoreZooming) - def lineCord(self,src,des,type_no,itemignoreZooming): + self.lineCord(src,des,items,itemignoreZooming,linetype) + + def lineCord(self,src,des,type_no,itemignoreZooming,linetype): srcdes_list = [] endtype = type_no[1] line = 0 @@ -810,18 +829,18 @@ class KineticsWidget(EditorWidgetBase): return srcdes_list = [src,des,endtype,line] arrow = calcArrow(srcdes_list,itemignoreZooming,self.iconScale) - self.drawLine(srcdes_list,arrow) + self.drawLine(srcdes_list,arrow,linetype) while(type_no[2] > 1 and line <= (type_no[2]-1)): srcdes_list =[src,des,endtype,line] arrow = calcArrow(srcdes_list,itemignoreZooming,self.iconScale) - self.drawLine(srcdes_list,arrow) + self.drawLine(srcdes_list,arrow,linetype) line = line +1 if type_no[2] > 5: print ("Higher order reaction will not be displayed") - def drawLine(self,srcdes_list,arrow): + def drawLine(self,srcdes_list,arrow,linetype="solid"): src = srcdes_list[0] des = srcdes_list[1] endtype = srcdes_list[2] @@ -839,7 +858,12 @@ class KineticsWidget(EditorWidgetBase): qgLineitem = self.sceneContainer.addPolygon(arrow) qgLineitem.setParentItem(src.parentItem()) pen = QtGui.QPen(QtCore.Qt.green, 0, Qt.Qt.SolidLine, Qt.Qt.RoundCap, Qt.Qt.RoundJoin) + if linetype == "crosscompt": + pen.setStyle(Qt.Qt.CustomDashLine) + pen.setDashPattern([1, 5, 1, 5]) + pen.setWidth(self.arrowsize) + # Green is default color moose.ReacBase and derivatives - already set above if isinstance(source, EnzBase): if ( (endtype == 's') or (endtype == 'p')): @@ -859,8 +883,37 @@ class KineticsWidget(EditorWidgetBase): self.object2line[ src ].append( ( qgLineitem, des,endtype,line) ) self.object2line[ des ].append( ( qgLineitem, src,endtype,line ) ) qgLineitem.setPen(pen) - + def positionChange(self,mooseObject): + #If the item position changes, the corresponding arrow's are calculated + print mooseObject + for k, v in self.qGraCompt.items(): + for rectChilditem in v.childItems(): + self.updateArrow(rectChilditem) + if isinstance(rectChilditem,GRPItem): + for grpChilditem in rectChilditem.childItems(): + self.updateArrow(grpChilditem) + if isinstance(grpChilditem,KineticsDisplayItem): + if moose.exists(grpChilditem.mobj.path+'/info'): + #print grpChilditem.mobj.name, grpChilditem.scenePos() + moose.element(grpChilditem.mobj.path+'/info').x = grpChilditem.scenePos().x() + moose.element(grpChilditem.mobj.path+'/info').y = grpChilditem.scenePos().y() + #self.updateGrpSize(rectChilditem) + if isinstance(rectChilditem,KineticsDisplayItem): + if moose.exists(rectChilditem.mobj.path+'/info'): + #print rectChilditem.mobj.name, rectChilditem.scenePos() + moose.element(rectChilditem.mobj.path+'/info').x = rectChilditem.scenePos().x() + moose.element(rectChilditem.mobj.path+'/info').y = rectChilditem.scenePos().y() + + rectcompt = calculateChildBoundingRect(v) + comptBoundingRect = v.boundingRect() + if not comptBoundingRect.contains(rectcompt): + self.updateCompartmentSize(v) + else: + rectcompt = calculateChildBoundingRect(v) + v.setRect(rectcompt.x()-10,rectcompt.y()-10,(rectcompt.width()+20),(rectcompt.height()+20)) + + def positionChange_old(self,mooseObject): #If the item position changes, the corresponding arrow's are calculated if isinstance(element(mooseObject),ChemCompt): for k, v in self.qGraCompt.items(): @@ -868,15 +921,23 @@ class KineticsWidget(EditorWidgetBase): if k.path == mesh: for rectChilditem in v.childItems(): if isinstance(rectChilditem, KineticsDisplayItem): - if isinstance(moose.element(rectChilditem.mobj.path),PoolBase): - t = moose.element(rectChilditem.mobj.path) - moose.element(t).children - for items in moose.element(t).children: - if isinstance(moose.element(items),Function): - test = moose.element(items.path+'/x') - for i in moose.element(test).neighbors['input']: - j = self.mooseId_GObj[moose.element(i)] - self.updateArrow(j) + if moose.exists(rectChilditem.mobj.path): + iInfo = rectChilditem.mobj.path+'/info' + anno = moose.Annotator(iInfo) + #storing scenePos back to annotator file for further use + x = rectChilditem.scenePos().x() + y = rectChilditem.scenePos().y() + #anno.x = x + #anno.y = y + if isinstance(moose.element(rectChilditem.mobj.path),PoolBase): + t = moose.element(rectChilditem.mobj.path) + moose.element(t).children + for items in moose.element(t).children: + if isinstance(moose.element(items),Function): + test = moose.element(items.path+'/x') + for i in moose.element(test).neighbors['input']: + j = self.mooseId_GObj[moose.element(i)] + self.updateArrow(j) self.updateArrow(rectChilditem) elif element(mooseObject).className == 'Neutral': for k,v in self.qGraGrp.items(): @@ -886,14 +947,10 @@ class KineticsWidget(EditorWidgetBase): iInfo = grpChilditem.mobj.path+'/info' anno = moose.Annotator(iInfo) #storing scenePos back to annotator file for further use - if moose.Annotator(self.plugin.modelRoot+'/info').modeltype == 'kkit': - x = grpChilditem.scenePos().x()/self.defaultScenewidth - y = grpChilditem.scenePos().y()/self.defaultSceneheight - else: - x = grpChilditem.scenePos().x() - y = grpChilditem.scenePos().y() - anno.x = x - anno.y = y + x = grpChilditem.scenePos().x() + y = grpChilditem.scenePos().y() + #anno.x = x + #anno.y = y if isinstance(moose.element(grpChilditem.mobj.path),PoolBase): t = moose.element(grpChilditem.mobj.path) @@ -922,18 +979,20 @@ class KineticsWidget(EditorWidgetBase): else: mobj = self.mooseId_GObj[element(mooseObject)] self.updateArrow(mobj) - elePath = moose.element(mooseObject).path - pos = elePath.find('/',1) - l = elePath[0:pos] - linfo = moose.Annotator(l+'/info') - if moose.exists(l): - #anno = moose.Annotator(linfo) - if moose.Annotator(self.plugin.modelRoot+'/info').modeltype == 'kkit': - x = mobj.scenePos().x()/self.defaultScenewidth - y = mobj.scenePos().y()/self.defaultSceneheight - else: - x = mobj.scenePos().x() - y = mobj.scenePos().y() + # elePath = moose.element(mooseObject).path + # pos = elePath.find('/',1) + # l = elePath[0:pos] + # linfo = moose.Annotator(l+'/info') + # if moose.exists(l): + # #anno = moose.Annotator(linfo) + # # if moose.Annotator(self.plugin.modelRoot+'/info').modeltype == 'kkit': + # # x = mobj.scenePos().x()/self.defaultScenewidth + # # y = mobj.scenePos().y()/self.defaultSceneheight + # # else: + # # x = mobj.scenePos().x() + # # y = mobj.scenePos().y() + # x = mobj.scenePos().x() + # y = mobj.scenePos().y() #print " x and y at 863 ",mobj.scenePos() # for gk,gv in self.qGraGrp.items(): # rectgrp = calculateChildBoundingRect(gv) @@ -952,7 +1011,11 @@ class KineticsWidget(EditorWidgetBase): else: rectcompt = calculateChildBoundingRect(v) v.setRect(rectcompt.x()-10,rectcompt.y()-10,(rectcompt.width()+20),(rectcompt.height()+20)) - + # print " position change " + # for item in self.sceneContainer.items(): + # if isinstance(item,KineticsDisplayItem): + # print item.mobj.name, moose.element(item.mobj.path+'/info').x,moose.element(item.mobj.path+'/info').y + def updateGrpSize(self,grp): compartmentBoundary = grp.rect() diff --git a/moose-gui/plugins/kkitOrdinateUtil.py b/moose-gui/plugins/kkitOrdinateUtil.py index dd3b967d4b17c4fa94a9ad0e8130b724ed876c6c..4d27c9ad5f6909056ea8311642c41694810652cb 100644 --- a/moose-gui/plugins/kkitOrdinateUtil.py +++ b/moose-gui/plugins/kkitOrdinateUtil.py @@ -5,9 +5,13 @@ __version__ = "1.0.0" __maintainer__ = "HarshaRani" __email__ = "hrani@ncbs.res.in" __status__ = "Development" -__updated__ = "Oct 18 2017" +__updated__ = "Oct 26 2018" ''' +2018 +Oct 26: xfer molecules are not put into screen +Sep 28: to zoom the kkit co-ordinates a factor of w=1000 and h=800 is multipled here +2017 Oct 18: moved some function to kkitUtil getxyCord, etc function are added ''' @@ -20,6 +24,8 @@ import numpy as np import networkx as nx from kkitUtil import getRandColor,colorCheck,findCompartment, findGroup, findGroup_compt, mooseIsInstance from PyQt4.QtGui import QColor +import re +import moose._moose as moose def getxyCord(xcord,ycord,list1): for item in list1: @@ -198,20 +204,20 @@ def setupMeshObj(modelRoot): # n =n +1 for compt in wildcardFind(modelRoot+'/##[ISA=ChemCompt]'): for m in wildcardFind(compt.path+'/##[ISA=PoolBase]'): - grp_cmpt = findGroup_compt(m) - - xcord.append(xyPosition(m.path+'/info','x')) - ycord.append(xyPosition(m.path+'/info','y')) - if isinstance(element(grp_cmpt),Neutral): - if isinstance(element(m.parent),EnzBase): - populateMeshEntry(meshEntry,grp_cmpt,"cplx",m) - else: - populateMeshEntry(meshEntry,grp_cmpt,"pool",m) - else: - if isinstance(element(m.parent),EnzBase): - populateMeshEntry(meshEntry,compt,"cplx",m) + if not re.search("xfer",m.name): + grp_cmpt = findGroup_compt(m) + xcord.append(xyPosition(m.path+'/info','x')) + ycord.append(xyPosition(m.path+'/info','y')) + if isinstance(element(grp_cmpt),Neutral): + if isinstance(element(m.parent),EnzBase): + populateMeshEntry(meshEntry,grp_cmpt,"cplx",m) + else: + populateMeshEntry(meshEntry,grp_cmpt,"pool",m) else: - populateMeshEntry(meshEntry,compt,"pool",m) + if isinstance(element(m.parent),EnzBase): + populateMeshEntry(meshEntry,compt,"cplx",m) + else: + populateMeshEntry(meshEntry,compt,"pool",m) for r in wildcardFind(compt.path+'/##[ISA=ReacBase]'): rgrp_cmpt = findGroup_compt(r) @@ -384,7 +390,6 @@ def countitems(mitems,objtype): return(uniqItems,countuniqItems) def recalculatecoordinatesforKkit(mObjlist,xcord,ycord): - positionInfoExist = not(len(np.nonzero(xcord)[0]) == 0 \ and len(np.nonzero(ycord)[0]) == 0) @@ -399,8 +404,8 @@ def recalculatecoordinatesforKkit(mObjlist,xcord,ycord): if moose.exists(objInfo): Ix = (xyPosition(objInfo,'x')-xmin)/(xmax-xmin) Iy = (ymin-xyPosition(objInfo,'y'))/(ymax-ymin) - element(objInfo).x = Ix - element(objInfo).y = Iy + element(objInfo).x = Ix*1000 + element(objInfo).y = Iy*800 def xyPosition(objInfo,xory): try: diff --git a/moose-gui/plugins/kkitUtil.py b/moose-gui/plugins/kkitUtil.py index b7dde1b7cc061f53c9075db806ac4fce9ba32117..8aee47ee97d7df30766ad58b3694a3c40706c4fe 100644 --- a/moose-gui/plugins/kkitUtil.py +++ b/moose-gui/plugins/kkitUtil.py @@ -5,10 +5,12 @@ __version__ = "1.0.0" __maintainer__ = "HarshaRani" __email__ = "hrani@ncbs.res.in" __status__ = "Development" -__updated__ = "Sep 17 2018" +__updated__ = "Oct 11 2018" ''' 2018 +Oct 11: when collision is handled an update of position is done +Sep 28: spell corrected cyclMesh to cylMesh Sep 17: when vertical or horizontal layout is applied for group, compartment size is recalculated Sep 11: group size is calculated based on sceneBoundingRect for compartment size 2017 @@ -131,7 +133,6 @@ def moveX(reference, collider, layoutPt, margin): layoutPt.drawLine_arrow(itemignoreZooming=False) def handleCollisions(compartments, moveCallback, layoutPt,margin = 5.0): - print " handelCollision" if len(compartments) is 0 : return compartments = sorted(compartments, key = lambda c: c.sceneBoundingRect().center().x()) print " compartment ",compartments @@ -155,6 +156,7 @@ def handleCollisions(compartments, moveCallback, layoutPt,margin = 5.0): else: rectcompt = calculateChildBoundingRect(v) v.setRect(rectcompt.x()-10,rectcompt.y()-10,(rectcompt.width()+20),(rectcompt.height()+20)) + layoutPt.positionChange(compartments) return handleCollisions(compartments, moveCallback, layoutPt,margin) def calculateChildBoundingRect(compt): @@ -208,7 +210,7 @@ def mooseIsInstance(melement, classNames): return element(melement).__class__.__name__ in classNames def findCompartment(melement): - while not mooseIsInstance(melement, ["CubeMesh", "CyclMesh"]): + while not mooseIsInstance(melement, ["CubeMesh", "CylMesh"]): melement = melement.parent return melement @@ -218,6 +220,6 @@ def findGroup(melement): return melement def findGroup_compt(melement): - while not (mooseIsInstance(melement, ["Neutral","CubeMesh", "CyclMesh"])): + while not (mooseIsInstance(melement, ["Neutral","CubeMesh", "CylMesh"])): melement = melement.parent - return melement \ No newline at end of file + return melement diff --git a/moose-gui/plugins/kkitViewcontrol.py b/moose-gui/plugins/kkitViewcontrol.py index 06fa8b7f9cca58f97379d0af03360736abe6e311..877f305e4a61cc2d3774365f7c8e5351149491c6 100644 --- a/moose-gui/plugins/kkitViewcontrol.py +++ b/moose-gui/plugins/kkitViewcontrol.py @@ -5,11 +5,15 @@ __version__ = "1.0.0" __maintainer__ = "HarshaRani" __email__ = "hrani@ncbs.res.in" __status__ = "Development" -__updated__ = "Sep 7 2018" +__updated__ = "Oct 27 2018" ''' 2018 - +Oct 27 : When group is moved within another group, outer group is resize inturn compartmet is also resized +Oct 10 : Groups are handled with collision detection, + messagebox when object moved from one group to another + layout updated when object moved etc +Sep 18 : one can close the messagebox if doesn't want to plot Sep 07 : when object qgraphicalparent is changed then connecting arrow's parent also need to be changed Jun 08 : If object is moved from one group or compartment to another group or with in same Compartment, then both at moose level (group or compartment path is updated ) and qt level the setParentItem is set @@ -34,6 +38,8 @@ from kkitUtil import * #from setsolver import * from PyQt4 import QtSvg from moose import utils +from functools import partial + class GraphicalView(QtGui.QGraphicsView): @@ -191,6 +197,18 @@ class GraphicalView(QtGui.QGraphicsView): else: #If right button clicked self.resetState() + # if itemType == GROUP_INTERIOR: + # print " self.layoutPt.qGraGrp ",item, item.childItems() + # grouplist = [] + # for i in item.childItems(): + # if isinstance(i,KineticsDisplayItem): + # grouplist.append(i) + # popupmenu = QtGui.QMenu('PopupMenu', self) + # popupmenu.addAction("LinearLayout", lambda : handleCollisions(list(grouplist), moveX, self.layoutPt)) + # popupmenu.addAction("VerticalLayout" ,lambda : handleCollisions(list(grouplist), moveMin, self.layoutPt )) + # popupmenu.exec_(self.mapToGlobal(event.pos())) + # self.layoutPt.updateGrpSize(item) + if itemType == GROUP_BOUNDARY: popupmenu = QtGui.QMenu('PopupMenu', self) popupmenu.addAction("DeleteGroup", lambda : self.deleteGroup(item,self.layoutPt)) @@ -267,6 +285,9 @@ class GraphicalView(QtGui.QGraphicsView): final = self.mapToScene(event.pos()) displacement = final - initial item.moveBy(displacement.x(), displacement.y()) + if isinstance(item.parentItem(),GRPItem): + self.layoutPt.updateGrpSize(item.parentItem()) + self.layoutPt.positionChange(item.mobj.path) self.state["press"]["pos"] = event.pos() @@ -333,7 +354,6 @@ class GraphicalView(QtGui.QGraphicsView): #if already built model then compartment size depends on max and min objects rectcompt = calculateChildBoundingRect(v) v.setRect(rectcompt.x()-10,rectcompt.y()-10,(rectcompt.width()+20),(rectcompt.height()+20)) - else: #When group is moved then compartment need to be update which is done here if isinstance(self.state["release"]["item"], KineticsDisplayItem): @@ -356,7 +376,6 @@ class GraphicalView(QtGui.QGraphicsView): movedGraphObj = self.state["press"]["item"].parent() if actionType == "move": - if itemType == EMPTY: self.objectpullback("Empty",item,movedGraphObj,xx,yy) @@ -377,7 +396,14 @@ class GraphicalView(QtGui.QGraphicsView): if moose.exists(grpCmpt.mobj.path+'/'+parentPool.name+'/'+movedGraphObj.name): self.objectpullback("Enzyme",grpCmpt,movedGraphObj,xx,yy) else: - self.moveObjSceneParent(grpCmpt,movedGraphObj,item.pos(),self.mapToScene(event.pos())) + reply = QtGui.QMessageBox.question(self, "Moving the Object",'Do want to move \'{movedGraphObj}\' \n from \'{parent}\' to \'{grpCmpt}\''.format(movedGraphObj=movedGraphObj.mobj.name,parent= movedGraphObj.parentItem().mobj.name,grpCmpt=grpCmpt.mobj.name), + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) + if reply == QtGui.QMessageBox.No: + movedGraphObj.moveBy(-xx,-yy) + self.layoutPt.updateArrow(movedGraphObj) + + else: + self.moveObjSceneParent(grpCmpt,movedGraphObj,item.pos(),self.mapToScene(event.pos())) else: self.objectpullback("Enzymeparent",grpCmpt,movedGraphObj,xx,yy) else: @@ -385,9 +411,32 @@ class GraphicalView(QtGui.QGraphicsView): if moose.exists(grpCmpt.mobj.path+'/'+movedGraphObj.mobj.name): self.objectpullback("All",grpCmpt,movedGraphObj,xx,yy) else: - self.moveObjSceneParent(grpCmpt,movedGraphObj,item.pos(),self.mapToScene(event.pos())) + reply = QtGui.QMessageBox.question(self, "Moving the Object",'Do want to move \'{movedGraphObj}\' \n from \'{parent}\' to \'{grpCmpt}\''.format(movedGraphObj=movedGraphObj.mobj.name,parent= movedGraphObj.parentItem().mobj.name,grpCmpt=grpCmpt.mobj.name), + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) + if reply == QtGui.QMessageBox.No: + movedGraphObj.moveBy(-xx,-yy) + self.layoutPt.updateArrow(movedGraphObj) + else: + self.moveObjSceneParent(grpCmpt,movedGraphObj,item.pos(),self.mapToScene(event.pos())) else: '''Same compt/grp to which it was belong to ''' + if isinstance(movedGraphObj,KineticsDisplayItem): + itemPath = movedGraphObj.mobj.path + if moose.exists(itemPath): + iInfo = itemPath+'/info' + anno = moose.Annotator(iInfo) + eventpos = self.mapToScene(event.pos()) + itempos = item.pos() + x = eventpos.x()+(15/2)#-itempos.x() + y = eventpos.y()+(2/2)#-itempos.y() + anno.x = x + anno.y = y + self.layoutPt.updateArrow(itemPath) + QtGui.QApplication.setOverrideCursor(QtGui.QCursor(Qt.Qt.ArrowCursor)) + self.layoutPt.updateGrpSize(movedGraphObj.parentItem()) + self.layoutPt.positionChange(movedGraphObj.mobj) + self.updateScale(self.iconScale) + if isinstance(grpCmpt,GRPItem): self.layoutPt.updateGrpSize(movedGraphObj.parentItem()) elif isinstance(grpCmpt,ComptItem): @@ -420,29 +469,18 @@ class GraphicalView(QtGui.QGraphicsView): elif actionType == "plot": element = moose.element(item.parent().mobj.path) if isinstance (element,moose.PoolBase): - if moose.exists(self.modelRoot+'/data/graph_0'): - self.graph = moose.element(self.modelRoot+'/data/graph_0') - else: - moose.Neutral(self.modelRoot+'/data') - moose.Neutral(self.modelRoot+'/data/graph_0') - self.graph = moose.element(self.modelRoot+'/data/graph_0') - plotType = "Conc" msgBox = QtGui.QMessageBox() msgBox.setText('What to plot?') - msgBox.addButton(QtGui.QPushButton('Number'), QtGui.QMessageBox.YesRole) - msgBox.addButton(QtGui.QPushButton('Concentration'), QtGui.QMessageBox.NoRole) - ret = msgBox.exec_() - if ret == 0: - plotType = "N" - tablePath = moose.utils.create_table_path(moose.element(self.modelRoot), self.graph, element, plotType) - table = moose.utils.create_table(tablePath, element, plotType,"Table2") - ''' - tablePath = utils.create_table_path(moose.element(self.modelRoot), self.graph, element, "Conc") - table = utils.create_table(tablePath, element, "Conc","Table2") - ''' - self.layoutPt.plugin.view.getCentralWidget().plotWidgetContainer.plotAllData() - reply = QtGui.QMessageBox.information(self, "plot Object","Plot is added to Graph1", - QtGui.QMessageBox.Ok) + self.pushButtonNumber = QtGui.QPushButton(('Number'))#, QtGui.QMessageBox.YesRole) + self.pushButtonConc = QtGui.QPushButton('Concentration')#, QtGui.QMessageBox.NoRole + self.pushButtonConc.setAutoDefault(False) + self.pushButtonNumber.setAutoDefault(False) + msgBox.addButton(self.pushButtonNumber,QtGui.QMessageBox.YesRole) + msgBox.addButton(self.pushButtonConc,QtGui.QMessageBox.NoRole) + msgBox.buttonClicked.connect(partial(self.onClicked, str(self.modelRoot),element)) + msgBox.exec_() + self.removeConnector() + elif actionType == "clone": if self.state["move"]["happened"]: QtGui.QApplication.setOverrideCursor(QtGui.QCursor(Qt.Qt.ArrowCursor)) @@ -552,7 +590,33 @@ class GraphicalView(QtGui.QGraphicsView): popupmenu.addAction("Move", lambda: self.moveSelections()) popupmenu.exec_(self.mapToGlobal(event.pos())) self.resetState() - + + def onClicked(self,modelRoot,element, btn): + self.modelRoot = modelRoot + if moose.exists(self.modelRoot+'/data/graph_0'): + self.graph = moose.element(self.modelRoot+'/data/graph_0') + else: + moose.Neutral(self.modelRoot+'/data') + moose.Neutral(self.modelRoot+'/data/graph_0') + self.graph = moose.element(self.modelRoot+'/data/graph_0') + + plotType = "Conc" + if btn.text() == "Number": + plotType = "N" + else: + plotType = "Conc" + + tablePath = moose.utils.create_table_path(moose.element(self.modelRoot), self.graph, element, plotType) + table = moose.utils.create_table(tablePath, element, plotType,"Table2") + ''' + tablePath = utils.create_table_path(moose.element(self.modelRoot), self.graph, element, "Conc") + table = utils.create_table(tablePath, element, "Conc","Table2") + ''' + self.layoutPt.plugin.view.getCentralWidget().plotWidgetContainer.plotAllData() + reply = QtGui.QMessageBox.information(self, "plot Object","Plot is added to Graph1", + QtGui.QMessageBox.Ok) + self.removeConnector() + def objectpullback(self,messgtype,item,movedGraphObj,xx,yy): if messgtype.lower() != "empty": desObj = item.mobj.className @@ -601,43 +665,33 @@ class GraphicalView(QtGui.QGraphicsView): if isinstance(moose.element(es), EnzBase): if moose.element(moose.element(es).neighbors['enzDest'][0]) == movedGraphObj.mobj: enzGrapObj = self.layoutPt.mooseId_GObj[moose.element(es)] - testx = enzGrapObj.scenePos().x() - testy = enzGrapObj.scenePos().y() + enzXpos = enzGrapObj.scenePos().x() + enzYpos = enzGrapObj.scenePos().y() enzGrapObj.setParentItem(item) - enzGrapObj.setGeometry(testx,testy, + enzGrapObj.setGeometry(enzXpos,enzYpos, enzGrapObj.gobj.boundingRect().width(), enzGrapObj.gobj.boundingRect().height()) for ll in self.layoutPt.object2line[enzGrapObj]: ll[0].setParentItem(item) self.layoutPt.updateArrow(enzGrapObj) - #enzGrapObj.setGeometry(testx,testy, - # enzGrapObj.gobj.boundingRect().width(), - # enzGrapObj.gobj.boundingRect().height()) ''' Re-calculting the group size after the movement ''' + self.setnewPostion(movedGraphObj,itempos,eventpos) + self.layoutPt.updateArrow(movedGraphObj) + self.layoutPt.positionChange(movedGraphObj.mobj) if isinstance(prevPar,GRPItem): if item != prevPar: + self.layoutPt.updateGrpSize(prevPar) self.layoutPt.updateGrpSize(item) - self.setnewPostion(movedGraphObj,itempos,eventpos) - self.layoutPt.updateArrow(movedGraphObj) - + def setnewPostion(self,movedGraphObj,itempos,eventpos): if isinstance(movedGraphObj,KineticsDisplayItem): itemPath = movedGraphObj.mobj.path if moose.exists(itemPath): iInfo = itemPath+'/info' anno = moose.Annotator(iInfo) - # eventpos = self.mapToScene(event.pos()) - # itempos = item.pos() x = eventpos.x()+(15/2)-itempos.x() y = eventpos.y()+(2/2)-itempos.y() - if moose.Annotator(self.layoutPt.plugin.modelRoot+'/info').modeltype == 'kkit': - anno.x = x/self.layoutPt.defaultScenewidth - anno.y = y/self.layoutPt.defaultSceneheight - else: - anno.x = x - anno.y = y - #item = movedGraphObj if isinstance(movedGraphObj,ReacItem) or isinstance(movedGraphObj,EnzItem) or isinstance(movedGraphObj,MMEnzItem): movedGraphObj.setGeometry(x,y, movedGraphObj.gobj.boundingRect().width(), @@ -865,6 +919,7 @@ class GraphicalView(QtGui.QGraphicsView): #if ( isinstance(v, PoolItem) or isinstance(v, ReacItem) or isinstance(v, EnzItem) or isinstance(v, CplxItem) ): if isinstance(v,KineticsDisplayItem): v.setFlag(QtGui.QGraphicsItem.ItemIgnoresTransformations, on) + def keyPressEvent(self,event): key = event.key() self.removeConnector() @@ -899,20 +954,6 @@ class GraphicalView(QtGui.QGraphicsView): item.refresh(scale) xpos = item.pos().x() ypos = item.pos().y() - if isinstance(item,ReacItem) or isinstance(item,EnzItem) or isinstance(item,MMEnzItem): - item.setGeometry(xpos,ypos, - item.gobj.boundingRect().width(), - item.gobj.boundingRect().height()) - elif isinstance(item,CplxItem): - item.setGeometry(item.gobj.boundingRect().width()/2,item.gobj.boundingRect().height(), - item.gobj.boundingRect().width(), - item.gobj.boundingRect().height()) - elif isinstance(item,PoolItem) or isinstance(item, PoolItemCircle): - item.setGeometry(xpos, ypos,item.gobj.boundingRect().width() - +PoolItem.fontMetrics.width(' '), - item.gobj.boundingRect().height()) - item.bg.setRect(0, 0, item.gobj.boundingRect().width()+PoolItem.fontMetrics.width(' '), item.gobj.boundingRect().height()) - self.layoutPt.drawLine_arrow(itemignoreZooming=False) self.layoutPt.comptChildrenBoundingRect()