From a30549936fc1f3ae6b1c765291998283c08bb668 Mon Sep 17 00:00:00 2001 From: Dilawar Singh <dilawars@ncbs.res.in> Date: Sat, 8 Sep 2018 09:57:48 +0530 Subject: [PATCH] Squashed 'moose-core/' changes from d5b330dc02..d229eba6bb d229eba6bb Parallel solvers (#293) git-subtree-dir: moose-core git-subtree-split: d229eba6bbc957c97bac44d24d513543956942c1 --- .travis/travis_build_linux.sh | 12 +- CMakeLists.txt | 15 +- CheckCXXCompiler.cmake | 10 +- basecode/FuncOrder.h | 42 +- basecode/global.cpp | 17 + basecode/global.h | 15 + biophysics/ReadSwc.cpp | 469 +++-- builtins/Function.cpp | 49 +- builtins/Function.h | 12 +- diffusion/Dsolve.cpp | 1764 +++++++++-------- diffusion/Dsolve.h | 388 ++-- external/muparser/include/muParserBase.h | 2 +- hsolve/HSolve.cpp | 22 +- hsolve/HSolve.h | 223 ++- ksolve/BoostSys.h | 28 - ksolve/CMakeLists.txt | 12 +- ksolve/FuncRateTerm.h | 221 +-- ksolve/FuncTerm.cpp | 114 +- ksolve/FuncTerm.h | 73 +- ksolve/Gsolve.cpp | 288 +-- ksolve/Gsolve.h | 15 +- ksolve/GssaVoxelPools.cpp | 32 +- ksolve/Ksolve.cpp | 183 +- ksolve/Ksolve.h | 21 +- ksolve/OdeSystem.h | 2 - ksolve/VoxelPools.cpp | 188 +- ksolve/VoxelPools.h | 13 +- ksolve/ZombiePoolInterface.cpp | 33 +- ksolve/ZombiePoolInterface.h | 215 +- mesh/CylMesh.cpp | 1457 +++++++------- pymoose/PyRun.cpp | 187 +- pymoose/PyRun.h | 80 +- pymoose/moosemodule.cpp | 24 +- pymoose/pymooseinit.cpp | 344 ++-- python/moose/utils.py | 121 +- python/rdesigneur/rdesigneur.py | 524 ++--- shell/Shell.cpp | 5 + shell/Wildcard.cpp | 13 - ...aupnerBrunel2012CaPlasticitySynHandler.cpp | 2 - tests/python/fixXreacs.py | 194 -- tests/python/testXchan1.py | 2 +- tests/python/testXdiff1.py | 2 +- tests/python/testXenz1.py | 2 +- tests/python/testXreacs2.py | 2 +- tests/python/testXreacs3.py | 2 +- tests/python/testXreacs4.py | 2 +- tests/python/testXreacs4a.py | 2 +- tests/python/testXreacs5.py | 2 +- tests/python/testXreacs5a.py | 2 +- tests/python/testXreacs6.py | 2 +- tests/python/testXreacs7.py | 2 +- tests/python/testXreacs8.py | 2 +- tests/python/test_Xchan1.py | 2 +- tests/python/test_Xdiff1.py | 2 +- tests/python/test_Xenz1.py | 2 +- tests/python/test_Xreacs2.py | 2 +- tests/python/test_Xreacs3.py | 2 +- tests/python/test_Xreacs4.py | 2 +- tests/python/test_Xreacs4a.py | 2 +- tests/python/test_Xreacs5.py | 8 +- tests/python/test_Xreacs5a.py | 2 +- tests/python/test_Xreacs6.py | 2 +- tests/python/test_Xreacs7.py | 2 +- tests/python/test_Xreacs8.py | 2 +- .../test_cylinder_diffusion_gsolve+dsolve.py | 146 ++ tests/python/test_gsolve_parallel.py | 83 + tests/python/test_ksolve.py | 1 + tests/python/test_ksolve_parallel.py | 105 + utility/print_function.hpp | 2 +- 69 files changed, 4020 insertions(+), 3800 deletions(-) delete mode 100644 tests/python/fixXreacs.py create mode 100644 tests/python/test_cylinder_diffusion_gsolve+dsolve.py create mode 100644 tests/python/test_gsolve_parallel.py create mode 100644 tests/python/test_ksolve_parallel.py diff --git a/.travis/travis_build_linux.sh b/.travis/travis_build_linux.sh index a4499d30..3352c2f0 100755 --- a/.travis/travis_build_linux.sh +++ b/.travis/travis_build_linux.sh @@ -44,7 +44,7 @@ echo "Currently in `pwd`" ( mkdir -p _GSL_BUILD && cd _GSL_BUILD cmake -DDEBUG=ON -DPYTHON_EXECUTABLE="$PYTHON2" .. - $MAKE && ctest --output-on-failure -j4 + $MAKE && ctest --output-on-failure sudo make install && cd /tmp $PYTHON2 -c 'import moose;print(moose.__file__);print(moose.version())' ) @@ -53,7 +53,7 @@ echo "Currently in `pwd`" # Now with boost. mkdir -p _BOOST_BUILD && cd _BOOST_BUILD && \ cmake -DWITH_BOOST_ODE=ON -DDEBUG=ON -DPYTHON_EXECUTABLE="$PYTHON2" .. - $MAKE && ctest --output-on-failure -j4 + $MAKE && ctest --output-on-failure ) # This is only applicable on linux build. @@ -63,13 +63,13 @@ if type $PYTHON3 > /dev/null; then sudo apt-get install -qq python3-networkx || echo "Error with apt" ( mkdir -p _GSL_BUILD2 && cd _GSL_BUILD2 && \ - cmake -DDEBUG=ON -DPYTHON_EXECUTABLE="$PYTHON3" .. - $MAKE && ctest --output-on-failure -j4 + cmake -DPYTHON_EXECUTABLE="$PYTHON3" .. + $MAKE && ctest --output-on-failure ) ( mkdir -p _BOOST_BUILD2 && cd _BOOST_BUILD2 && \ - cmake -DWITH_BOOST_ODE=ON -DDEBUG=ON -DPYTHON_EXECUTABLE="$PYTHON3" .. - $MAKE && ctest --output-on-failure -j4 + cmake -DWITH_BOOST_ODE=ON -DPYTHON_EXECUTABLE="$PYTHON3" .. + $MAKE && ctest --output-on-failure ) echo "All done" else diff --git a/CMakeLists.txt b/CMakeLists.txt index 59977a0f..3bfb7fc8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,11 +80,10 @@ option(DEBUG "Build with debug support" OFF) option(GPROF "Build for profiling using gprof" OFF) option(ENABLE_UNIT_TESTS "Enable unit tests (DEBUG should also be ON)" OFF) option(WITH_MPI "Enable Openmpi support" OFF) +option(WITH_BOOST "Enable boost. Prefer boost over stl" OFF) option(WITH_BOOST_ODE "Use boost library ode2 library instead of GSL" OFF) option(WITH_GSL "Use gsl-library. Alternative is WITH_BOOST" ON) -option(PARALLELIZED_SOLVERS "Use parallel version of GSOLVE. (alpha)" OFF ) option(PARALLELIZED_CLOCK "High level parallelization of moose::Clock (alpha)" OFF ) - option(USE_PRIVATE_RNG "Stochastic Objects use their private RNG" ON) @@ -116,9 +115,7 @@ if(GPROF AND "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") set(CMAKE_EXE_LINKER_FLAGS_DEBUG "-pg") endif() -if(PARALLELIZED_SOLVERS) - find_package(Threads) -endif() +find_package(Threads) ################################### TARGETS #################################### @@ -131,6 +128,10 @@ add_executable(moose.bin basecode/main.cpp) # default include paths. include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ) +if(WITH_BOOST) + set(WITH_BOOST_ODE ON) +endif(WITH_BOOST) + # If using BOOST ODE2 library to solve ODE system, then don't use GSL. if(WITH_BOOST_ODE) set(WITH_GSL OFF) @@ -269,9 +270,7 @@ if(WITH_BOOST_ODE) list(APPEND SYSTEM_SHARED_LIBS ${Boost_LIBRARIES}) endif(WITH_BOOST_ODE) -if(PARALLELIZED_SOLVERS) - list(APPEND SYSTEM_SHARED_LIBS ${CMAKE_THREAD_LIBS_INIT}) -endif() +list(APPEND SYSTEM_SHARED_LIBS ${CMAKE_THREAD_LIBS_INIT}) # These libraries could be static of dynamic. We need to discrimate between # these two types because of --whole-archive option. See diff --git a/CheckCXXCompiler.cmake b/CheckCXXCompiler.cmake index f3fe24d9..c6173acb 100644 --- a/CheckCXXCompiler.cmake +++ b/CheckCXXCompiler.cmake @@ -11,9 +11,11 @@ add_definitions(-Wall #-Wno-return-type-c-linkage -Wno-unused-variable -Wno-unused-function - -Wno-unused-local-typedefs #-Wno-unused-private-field ) +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + add_definitions( -Wno-unused-local-typedefs ) +endif() add_definitions(-fPIC) @@ -34,9 +36,9 @@ if(COMPILER_SUPPORTS_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") add_definitions( -DENABLE_CPP11 ) if(APPLE) - #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++" ) - message(STATUS "NOTE: Making clang to inline more aggresively" ) - add_definitions( -mllvm -inline-threshold=1000 ) + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++" ) + message(STATUS "NOTE: Making clang to inline more aggresively" ) + add_definitions( -mllvm -inline-threshold=1000 ) endif(APPLE) else(COMPILER_SUPPORTS_CXX11) add_definitions( -DBOOST_NO_CXX11_SCOPED_ENUMS -DBOOST_NO_SCOPED_ENUMS ) diff --git a/basecode/FuncOrder.h b/basecode/FuncOrder.h index 088b2f1f..03c4bfc1 100644 --- a/basecode/FuncOrder.h +++ b/basecode/FuncOrder.h @@ -13,28 +13,28 @@ class FuncOrder { - public: - FuncOrder() - : func_( 0 ), index_( 0 ) - {;} + public: + FuncOrder() + : func_( 0 ), index_( 0 ) + {;} - const OpFunc* func() const { - return func_; - } - unsigned int index() const { - return index_; - } + const OpFunc* func() const { + return func_; + } + unsigned int index() const { + return index_; + } - void set( const OpFunc* func, unsigned int index ) { - func_ = func; - index_ = index; - } + void set( const OpFunc* func, unsigned int index ) { + func_ = func; + index_ = index; + } - bool operator<( const FuncOrder& other ) const - { - return func_ < other.func_; - } - private: - const OpFunc* func_; - unsigned int index_; + bool operator<( const FuncOrder& other ) const + { + return func_ < other.func_; + } + private: + const OpFunc* func_; + unsigned int index_; }; diff --git a/basecode/global.cpp b/basecode/global.cpp index 90f82b6b..fadf41ef 100644 --- a/basecode/global.cpp +++ b/basecode/global.cpp @@ -44,6 +44,11 @@ namespace moose { unsigned long __rng_seed__ = 0; + map<string, valarray<double>> solverProfMap = { + { "Ksolve", {0.0, 0} }, + { "HSolve", {0.0, 0} } + }; + moose::RNG<double> rng; /* Check if path is OK */ @@ -212,4 +217,16 @@ namespace moose { { __rng_seed__ = seed; } + + void addSolverProf( const string& name, double time, size_t steps) + { + solverProfMap[ name ] = solverProfMap[name] + valarray<double>({ time, (double)steps }); + } + + void printSolverProfMap( ) + { + for( auto &v : solverProfMap ) + cout << '\t' << v.first << ": " << v.second[0] << " sec (" << v.second[1] << ")" << endl; + } + } diff --git a/basecode/global.h b/basecode/global.h index 1735595d..893c5576 100644 --- a/basecode/global.h +++ b/basecode/global.h @@ -15,6 +15,7 @@ #include <ctime> #include <map> #include <sstream> +#include <valarray> #include "../randnum/RNG.h" /* Use inbuilt rng */ #include "../utility/print_function.hpp" @@ -57,6 +58,8 @@ namespace moose extern moose::RNG<double> rng; + extern map<string, valarray<double>> solverProfMap; + /** * @brief A global seed for all RNGs in moose. When moose.seed( x ) is called, * this variable is set. Other's RNGs (except muparser) uses this seed to @@ -194,6 +197,18 @@ namespace moose */ /* ----------------------------------------------------------------------------*/ void setGlobalSeed( int seed ); + + /* --------------------------------------------------------------------------*/ + /** + * @Synopsis Add solver performance into the global map. + * + * @Param name Name of the solver. + * @Param time Time taken by the solver. + * @Param steps Steps. + */ + /* ----------------------------------------------------------------------------*/ + void addSolverProf( const string& name, double time, size_t steps = 1); + void printSolverProfMap( ); } #endif /* ----- #ifndef __MOOSE_GLOBAL_INC_ ----- */ diff --git a/biophysics/ReadSwc.cpp b/biophysics/ReadSwc.cpp index fe03adb1..22cc98c8 100644 --- a/biophysics/ReadSwc.cpp +++ b/biophysics/ReadSwc.cpp @@ -16,6 +16,7 @@ #include "Compartment.h" #include "SymCompartment.h" #include <fstream> +#include <iomanip> // Minimum allowed radius of segment, in microns // Believe it or not, some otherwise reasonable files do have smaller radii @@ -23,260 +24,290 @@ static const double MinRadius = 0.04; ReadSwc::ReadSwc( const string& fname ) { - ifstream fin( fname.c_str() ); - if ( !fin ) { - cerr << "ReadSwc:: could not open file " << fname << endl; - return; - } + ifstream fin( fname.c_str() ); + if ( !fin ) + { + cerr << "ReadSwc:: could not open file " << fname << endl; + return; + } - string temp; - int badSegs = 0; - while( getline( fin, temp ) ) { - if ( temp.length() == 0 ) - continue; - string::size_type pos = temp.find_first_not_of( "\t " ); - if ( pos == string::npos ) - continue; - if ( temp[pos] == '#' ) - continue; + string temp; + int badSegs = 0; + while( getline( fin, temp ) ) + { + if ( temp.length() == 0 ) + continue; + string::size_type pos = temp.find_first_not_of( "\t " ); + if ( pos == string::npos ) + continue; + if ( temp[pos] == '#' ) + continue; - SwcSegment t( temp ); - if ( t.OK() ) - segs_.push_back( SwcSegment( temp ) ); - else - badSegs++; - } - bool valid = validate(); - if ( valid ) { - assignKids(); - cleanZeroLength(); - parseBranches(); - } - cout << "ReadSwc: " << fname << " : NumSegs = " << segs_.size() << - ", bad = " << badSegs << - ", Validated = " << valid << - ", numBranches = " << branches_.size() << - endl; - diagnostics(); + SwcSegment t( temp ); + if ( t.OK() ) + segs_.push_back( SwcSegment( temp ) ); + else + badSegs++; + } + bool valid = validate(); + if ( valid ) + { + assignKids(); + cleanZeroLength(); + parseBranches(); + } + cout << "ReadSwc: " << fname << " : NumSegs = " << segs_.size() << + ", bad = " << badSegs << + ", Validated = " << valid << + ", numBranches = " << branches_.size() << + endl; + diagnostics(); } bool ReadSwc::validate() const { - int numStart = 0; - int numOrphans = 0; - int badIndex = 0; - int badRadius = 0; - for ( unsigned int i = 0; i < segs_.size(); ++i ) { - const SwcSegment& s = segs_[i]; - if ( s.myIndex() != i + 1 ) - badIndex++; - if ( s.parent() == ~0U ) { - numStart++; - } else { - if ( s.parent() > i ) { - numOrphans++; - } - } - if ( s.radius() < MinRadius ) { - badRadius++; - } - } - bool valid = ( numStart == 1 && numOrphans == 0 && badRadius == 0 ); - if ( !valid ) { - cout << "ReadSwc::validate() failed: \nNumSegs = " << - segs_.size() << - ", numStart = " << numStart << - ", orphans = " << numOrphans << - ", badIndex = " << badIndex << - ", badRadius = " << badRadius << - ", numBranches = " << branches_.size() << - endl; - } - return valid; + int numStart = 0; + int numOrphans = 0; + int badIndex = 0; + int badRadius = 0; + for ( unsigned int i = 0; i < segs_.size(); ++i ) + { + const SwcSegment& s = segs_[i]; + if ( s.myIndex() != i + 1 ) + badIndex++; + if ( s.parent() == ~0U ) + { + numStart++; + } + else + { + if ( s.parent() > i ) + { + numOrphans++; + } + } + if ( s.radius() < MinRadius ) + { + badRadius++; + } + } + bool valid = ( numStart == 1 && numOrphans == 0 && badRadius == 0 ); + if ( !valid ) + { + cout << "ReadSwc::validate() failed: \nNumSegs = " << + segs_.size() << + ", numStart = " << numStart << + ", orphans = " << numOrphans << + ", badIndex = " << badIndex << + ", badRadius = " << badRadius << + ", numBranches = " << branches_.size() << + endl; + } + return valid; } void ReadSwc::assignKids() { - for ( unsigned int i = 0; i < segs_.size(); ++i ) { - const SwcSegment& s = segs_[i]; - assert ( s.parent() != s.myIndex() ); - if ( s.parent() != ~0U ) { - segs_[s.parent() - 1].addChild( i + 1 ); - } - } - for ( unsigned int i = 0; i < segs_.size(); ++i ) { - segs_[i].figureOutType(); - } + for ( unsigned int i = 0; i < segs_.size(); ++i ) + { + const SwcSegment& s = segs_[i]; + assert ( s.parent() != s.myIndex() ); + if ( s.parent() != ~0U ) + { + segs_[s.parent() - 1].addChild( i + 1 ); + } + } + for ( unsigned int i = 0; i < segs_.size(); ++i ) + { + segs_[i].figureOutType(); + } } void ReadSwc::cleanZeroLength() { - static double EPSILON = 1e-2; // Assume units in microns. - for ( unsigned int i = 1; i < segs_.size(); ++i ) { - SwcSegment& s = segs_[i]; - SwcSegment& pa = segs_[ s.parent() - 1 ]; - if ( s.distance( pa ) < EPSILON ) { - // Remove the zero length child from pa.kids_ - vector< int > temp; - for ( unsigned int j = 0; j < pa.kids().size(); ++j ) { - if ( static_cast< unsigned int >( pa.kids()[j] ) != s.myIndex() ) - temp.push_back( pa.kids()[j] ); - } - // Go through all kids of s and reparent them. - for ( unsigned int j = 0; j < s.kids().size(); ++j ) { - SwcSegment& kid = segs_[ s.kids()[j] - 1 ]; - kid.setParent( pa.myIndex() ); - temp.push_back( kid.myIndex() ); - } - pa.replaceKids( temp ); - s.setBad(); - cout << "ReadSwc:: Cleaned zero length " << s.myIndex() << endl; - } - } + static double EPSILON = 1e-2; // Assume units in microns. + for ( unsigned int i = 1; i < segs_.size(); ++i ) + { + SwcSegment& s = segs_[i]; + SwcSegment& pa = segs_[ s.parent() - 1 ]; + if ( s.distance( pa ) < EPSILON ) + { + // Remove the zero length child from pa.kids_ + vector< int > temp; + for ( unsigned int j = 0; j < pa.kids().size(); ++j ) + { + if ( static_cast< unsigned int >( pa.kids()[j] ) != s.myIndex() ) + temp.push_back( pa.kids()[j] ); + } + // Go through all kids of s and reparent them. + for ( unsigned int j = 0; j < s.kids().size(); ++j ) + { + SwcSegment& kid = segs_[ s.kids()[j] - 1 ]; + kid.setParent( pa.myIndex() ); + temp.push_back( kid.myIndex() ); + } + pa.replaceKids( temp ); + s.setBad(); + cout << "ReadSwc:: Cleaned zero length " << s.myIndex() << endl; + } + } } void ReadSwc::traverseBranch( const SwcSegment& s, - double& len, double& L, vector< int >& cable ) const + double& len, double& L, vector< int >& cable ) const { - const SwcSegment* prev = &s; - cable.resize( 1, s.myIndex() ); // Always include the starting seg. - // Note that the cable is filled up with entries in reverse order. + const SwcSegment* prev = &s; + cable.resize( 1, s.myIndex() ); // Always include the starting seg. + // Note that the cable is filled up with entries in reverse order. - if ( s.parent() == ~0U ) { - len = s.radius(); - L = sqrt( len ); - return ; - } + if ( s.parent() == ~0U ) + { + len = s.radius(); + L = sqrt( len ); + return ; + } - do { - // Beware the indexing! - const SwcSegment& pa = segs_[prev->parent() - 1]; - len += pa.distance( *prev ); - L += pa.L( ); - cable.push_back( pa.myIndex() ); - prev = &pa; - } while ( (prev->parent() != ~0U) && (prev->kids().size() == 1) ); - cable.pop_back(); // Get rid of the last entry, it is on the parent. + do + { + // Beware the indexing! + const SwcSegment& pa = segs_[prev->parent() - 1]; + len += pa.distance( *prev ); + L += pa.L( ); + cable.push_back( pa.myIndex() ); + prev = &pa; + } + while ( (prev->parent() != ~0U) && (prev->kids().size() == 1) ); + cable.pop_back(); // Get rid of the last entry, it is on the parent. } void ReadSwc::parseBranches() { - // Fill vector of all branches. - for ( unsigned int i = 0; i < segs_.size(); ++i ) { - const SwcSegment& s = segs_[i]; - if ( s.OK() && s.kids().size() != 1 ) { // Either use a fork or an end. - vector< int > cable; - // int branchIndex = branches_. - // branches_.push_back( i + 1 ); - double len = 0; - double L = 0; - traverseBranch( s, len, L, cable ); - // branchGeomLength_.push_back( len ); - // branchElectroLength_.push_back( L ); - SwcBranch br( branches_.size(), s, len, L, cable ); - branches_.push_back( br ); - } - } - // Assign the parent of each branch. This is known because the - // parent of the first segment in the branch is the last segment - // in the parent branch. I construct a reverse lookup table to find - // the branch # from its last segment number. - vector< int > reverseSeg ( segs_.size() + 1, 0 ); - for ( unsigned int i = 0; i < branches_.size(); ++i ) - reverseSeg[ branches_[i].segs_.back() ] = i; - for ( unsigned int i = 0; i < branches_.size(); ++i ) { - int parentSeg = segs_[ branches_[i].segs_[0] - 1 ].parent(); - assert( parentSeg != 0 ); // Note that segment indices start from 1 - branches_[i].setParent( reverseSeg[ parentSeg ] ); - } + // Fill vector of all branches. + for ( unsigned int i = 0; i < segs_.size(); ++i ) + { + const SwcSegment& s = segs_[i]; + if ( s.OK() && s.kids().size() != 1 ) // Either use a fork or an end. + { + vector< int > cable; + // int branchIndex = branches_. + // branches_.push_back( i + 1 ); + double len = 0; + double L = 0; + traverseBranch( s, len, L, cable ); + // branchGeomLength_.push_back( len ); + // branchElectroLength_.push_back( L ); + SwcBranch br( branches_.size(), s, len, L, cable ); + branches_.push_back( br ); + } + } + // Assign the parent of each branch. This is known because the + // parent of the first segment in the branch is the last segment + // in the parent branch. I construct a reverse lookup table to find + // the branch # from its last segment number. + vector< int > reverseSeg ( segs_.size() + 1, 0 ); + for ( unsigned int i = 0; i < branches_.size(); ++i ) + reverseSeg[ branches_[i].segs_.back() ] = i; + for ( unsigned int i = 0; i < branches_.size(); ++i ) + { + int parentSeg = segs_[ branches_[i].segs_[0] - 1 ].parent(); + assert( parentSeg != 0 ); // Note that segment indices start from 1 + branches_[i].setParent( reverseSeg[ parentSeg ] ); + } } void ReadSwc::diagnostics() const { - vector< int > diag( 14 ); - for ( unsigned int i = 0; i < segs_.size(); ++i ) { - const SwcSegment& s = segs_[i]; - if ( s.type() < 14 ) - diag[s.type()]++; - } - for ( int i = 0; i < 14; ++i ) - cout << "ReadSwc::diagnostics: " << SwcSegment::typeName[i] << " : " << diag[i] << endl; + vector< int > diag( 14 ); + for ( unsigned int i = 0; i < segs_.size(); ++i ) + { + const SwcSegment& s = segs_[i]; + if ( s.type() < 14 ) + diag[s.type()]++; + } + + for ( int i = 0; i < 14; ++i ) + cout << "ReadSwc::diagnostics: " << setw(12) << SwcSegment::typeName[i] + << ": " << setw(5) << diag[i] << endl; - /* - for ( unsigned int i = 0; i < branches_.size(); ++i ) - branches_[i].printDiagnostics(); - */ } static Id makeCompt( Id parent, - const SwcSegment& seg, const SwcSegment& pa, - double RM, double RA, double CM, - unsigned int i, unsigned int j ) + const SwcSegment& seg, const SwcSegment& pa, + double RM, double RA, double CM, + unsigned int i, unsigned int j ) { - Shell* shell = reinterpret_cast< Shell* >( Id().eref().data() ); - double len = seg.radius() * 2.0; - string name = "soma"; - Id compt; - double x0, y0, z0; - if ( seg.parent() != ~0U ) { - len = seg.distance( pa ); - stringstream ss; - ss << SwcSegment::typeName[ seg.type() ] << "_" << i << "_" << j; - name = ss.str(); - x0 = pa.vec().a0(); - y0 = pa.vec().a1(); - z0 = pa.vec().a2(); - } else { - x0 = seg.vec().a0() - len; - y0 = seg.vec().a1(); - z0 = seg.vec().a2(); - } - assert( len > 0.0 ); - compt = shell->doCreate( "Compartment", parent, name, 1 ); - Eref er = compt.eref(); - moose::CompartmentBase *cptr = reinterpret_cast< moose::CompartmentBase* >( - compt.eref().data() ); - double xa = seg.radius() * seg.radius() * PI * 1e-12; - len *= 1e-6; - double dia = seg.radius() * 2.0e-6; - cptr->setRm( er, RM / ( len * dia * PI ) ); - cptr->setRa( er, RA * len / xa ); - cptr->setCm( er, CM * ( len * dia * PI ) ); - cptr->setDiameter( dia ); - cptr->setLength( len ); - cptr->setX0( x0 * 1e-6 ); - cptr->setY0( y0 * 1e-6 ); - cptr->setZ0( z0 * 1e-6 ); - cptr->setX( seg.vec().a0() * 1e-6 ); - cptr->setY( seg.vec().a1() * 1e-6 ); - cptr->setZ( seg.vec().a2() * 1e-6 ); - return compt; + Shell* shell = reinterpret_cast< Shell* >( Id().eref().data() ); + double len = seg.radius() * 2.0; + string name = "soma"; + Id compt; + double x0, y0, z0; + if ( seg.parent() != ~0U ) + { + len = seg.distance( pa ); + stringstream ss; + ss << SwcSegment::typeName[ seg.type() ] << "_" << i << "_" << j; + name = ss.str(); + x0 = pa.vec().a0(); + y0 = pa.vec().a1(); + z0 = pa.vec().a2(); + } + else + { + x0 = seg.vec().a0() - len; + y0 = seg.vec().a1(); + z0 = seg.vec().a2(); + } + assert( len > 0.0 ); + compt = shell->doCreate( "Compartment", parent, name, 1 ); + Eref er = compt.eref(); + moose::CompartmentBase *cptr = reinterpret_cast< moose::CompartmentBase* >( + compt.eref().data() ); + double xa = seg.radius() * seg.radius() * PI * 1e-12; + len *= 1e-6; + double dia = seg.radius() * 2.0e-6; + cptr->setRm( er, RM / ( len * dia * PI ) ); + cptr->setRa( er, RA * len / xa ); + cptr->setCm( er, CM * ( len * dia * PI ) ); + cptr->setDiameter( dia ); + cptr->setLength( len ); + cptr->setX0( x0 * 1e-6 ); + cptr->setY0( y0 * 1e-6 ); + cptr->setZ0( z0 * 1e-6 ); + cptr->setX( seg.vec().a0() * 1e-6 ); + cptr->setY( seg.vec().a1() * 1e-6 ); + cptr->setZ( seg.vec().a2() * 1e-6 ); + return compt; } bool ReadSwc::build( Id parent, - double lambda, double RM, double RA, double CM ) + double lambda, double RM, double RA, double CM ) { - Shell* shell = reinterpret_cast< Shell* >( Id().eref().data() ); - vector< Id > compts( segs_.size() ); - for ( unsigned int i = 0; i < branches_.size(); ++i ) { - SwcBranch& br = branches_[i]; - for ( unsigned int j = 0; j < br.segs_.size(); ++j ) { - Id compt; - SwcSegment& seg = segs_[ br.segs_[j] -1 ]; - unsigned int paIndex = seg.parent(); - if ( paIndex == ~0U ) { // soma - compt = makeCompt( parent, seg, seg, RM, RA, CM, i, j ); - } else { - SwcSegment& pa = segs_[ paIndex - 1 ]; - compt = makeCompt( parent, seg, pa, RM, RA, CM, i, j ); - assert( compt != Id() ); - assert( compts[ paIndex -1 ] != Id() ); - shell->doAddMsg( "Single", - compts[paIndex-1], "axial", compt, "raxial" ); - } - assert( compt != Id() ); - compts[ seg.myIndex() -1 ] = compt; - } - } - return true; + Shell* shell = reinterpret_cast< Shell* >( Id().eref().data() ); + vector< Id > compts( segs_.size() ); + for ( unsigned int i = 0; i < branches_.size(); ++i ) + { + SwcBranch& br = branches_[i]; + for ( unsigned int j = 0; j < br.segs_.size(); ++j ) + { + Id compt; + SwcSegment& seg = segs_[ br.segs_[j] -1 ]; + unsigned int paIndex = seg.parent(); + if ( paIndex == ~0U ) // soma + { + compt = makeCompt( parent, seg, seg, RM, RA, CM, i, j ); + } + else + { + SwcSegment& pa = segs_[ paIndex - 1 ]; + compt = makeCompt( parent, seg, pa, RM, RA, CM, i, j ); + assert( compt != Id() ); + assert( compts[ paIndex -1 ] != Id() ); + shell->doAddMsg( "Single", + compts[paIndex-1], "axial", compt, "raxial" ); + } + assert( compt != Id() ); + compts[ seg.myIndex() -1 ] = compt; + } + } + return true; } diff --git a/builtins/Function.cpp b/builtins/Function.cpp index ea0c8ae8..8c82c64b 100644 --- a/builtins/Function.cpp +++ b/builtins/Function.cpp @@ -345,18 +345,6 @@ Function::Function(): _t(0.0), _valid(false), _numVar(0), _lastValue(0.0), _valid = true; } -#if 0 -void Function::extendMuParser( void ) -{ - // Adding pi and e, the defaults are `_pi` and `_e` - _parser.DefineConst(_T("pi"), (mu::value_type)M_PI); - _parser.DefineConst(_T("e"), (mu::value_type)M_E); - // Add support - _parser.DefineVar( _T("t"), &this->_t ); - _parser.DefineOprt( _T("%"), &Function::muCallbackFMod, 7, mu::EOprtAssociativity::oaRIGHT, 0); -} -#endif - Function::Function(const Function& rhs): _numVar(rhs._numVar), _lastValue(rhs._lastValue), _value(rhs._value), _rate(rhs._rate), @@ -484,8 +472,10 @@ double * _functionAddVar(const char *name, void *data) Function* function = reinterpret_cast< Function * >(data); double * ret = NULL; string strname(name); + // Names starting with x are variables, everything else is constant. - if (strname[0] == 'x'){ + if (strname[0] == 'x') + { int index = atoi(strname.substr(1).c_str()); if ((unsigned)index >= function->_varbuf.size()){ function->_varbuf.resize(index+1, 0); @@ -497,7 +487,9 @@ double * _functionAddVar(const char *name, void *data) function->_numVar = function->_varbuf.size(); } ret = &(function->_varbuf[index]->value); - } else if (strname[0] == 'y'){ + } + else if (strname[0] == 'y') + { int index = atoi(strname.substr(1).c_str()); if ((unsigned)index >= function->_pullbuf.size()){ function->_pullbuf.resize(index+1, 0 ); @@ -508,9 +500,13 @@ double * _functionAddVar(const char *name, void *data) } } ret = function->_pullbuf[index]; - } else if (strname == "t"){ + } + else if (strname == "t") + { ret = &function->_t; - } else { + } + else + { cerr << "Got an undefined symbol: " << name << endl << "Variables must be named xi, yi, where i is integer index." << " You must define the constants beforehand using LookupField c: c[name]" @@ -538,24 +534,12 @@ double * _functionAddVar(const char *name, void *data) */ unsigned int Function::addVar() { -// unsigned int newVarIndex = _numVar; -// ++_numVar; -// stringstream name; -// name << "x" << newVarIndex; -// _functionAddVar(name.str().c_str(), this); - // return newVarIndex; return 0; } -// void Function::dropVar(unsigned int msgLookup) -// { -// // Don't know what this can possibly mean in the context of -// // evaluating a set expression. -// } - void Function::setExpr(const Eref& eref, string expr) { - this->innerSetExpr( eref, expr ); // Refer to the virtual function here. + this->innerSetExpr( eref, expr ); // Refer to the virtual function here. } // Virtual function, this does the work. @@ -687,11 +671,8 @@ double Function::getDerivative() const void Function::setNumVar(const unsigned int num) { _clearBuffer(); - for (unsigned int ii = 0; ii < num; ++ii){ - stringstream name; - name << "x" << ii; - _functionAddVar(name.str().c_str(), this); - } + for (unsigned int ii = 0; ii < num; ++ii) + _functionAddVar( ("x"+std::to_string(ii)).c_str(), this); } unsigned int Function::getNumVar() const diff --git a/builtins/Function.h b/builtins/Function.h index 83f2b903..8f50957b 100644 --- a/builtins/Function.h +++ b/builtins/Function.h @@ -148,16 +148,22 @@ protected: unsigned int _mode; bool _useTrigger; bool _doEvalAtReinit; - // this stores variables received via incoming messages, identifiers of the form x{i} are included in this + + // this stores variables received via incoming messages, identifiers of + // the form x{i} are included in this vector<Variable *> _varbuf; - // this stores variable values pulled by sending request. identifiers of the form y{i} are included in this + + // this stores variable values pulled by sending request. identifiers of + // the form y{i} are included in this vector< double * > _pullbuf; map< string, double *> _constbuf; // for constants string _independent; // index of independent variable + mu::Parser _parser; + void _clearBuffer(); void _showError(mu::Parser::exception_type &e) const; - char* _stoich; // Used by kinetic solvers when this is zombified. + char* _stoich; // Used by kinetic solvers when this is zombified. }; diff --git a/diffusion/Dsolve.cpp b/diffusion/Dsolve.cpp index ec72a97e..c1428146 100644 --- a/diffusion/Dsolve.cpp +++ b/diffusion/Dsolve.cpp @@ -33,151 +33,154 @@ #include "../ksolve/ZombiePool.h" #include "../ksolve/ZombieBufPool.h" +#include <thread> + const Cinfo* Dsolve::initCinfo() { - /////////////////////////////////////////////////////// - // Field definitions - /////////////////////////////////////////////////////// - - static ValueFinfo< Dsolve, Id > stoich ( - "stoich", - "Stoichiometry object for handling this reaction system.", - &Dsolve::setStoich, - &Dsolve::getStoich - ); - - static ElementValueFinfo< Dsolve, string > path ( - "path", - "Path of reaction system. Must include all the pools that " - "are to be handled by the Dsolve, can also include other " - "random objects, which will be ignored.", - &Dsolve::setPath, - &Dsolve::getPath - ); - - static ReadOnlyValueFinfo< Dsolve, unsigned int > numVoxels( - "numVoxels", - "Number of voxels in the core reac-diff system, on the " - "current diffusion solver. ", - &Dsolve::getNumVoxels - ); - static ReadOnlyValueFinfo< Dsolve, unsigned int > numAllVoxels( - "numAllVoxels", - "Number of voxels in the core reac-diff system, on the " - "current diffusion solver. ", - &Dsolve::getNumVoxels - ); - static LookupValueFinfo< - Dsolve, unsigned int, vector< double > > nVec( - "nVec", - "vector of # of molecules along diffusion length, " - "looked up by pool index", - &Dsolve::setNvec, - &Dsolve::getNvec - ); - - static ValueFinfo< Dsolve, unsigned int > numPools( - "numPools", - "Number of molecular pools in the entire reac-diff system, " - "including variable, function and buffered.", - &Dsolve::setNumPools, - &Dsolve::getNumPools - ); - - static ValueFinfo< Dsolve, Id > compartment ( - "compartment", - "Reac-diff compartment in which this diffusion system is " - "embedded.", - &Dsolve::setCompartment, - &Dsolve::getCompartment - ); - - static LookupValueFinfo< Dsolve, unsigned int, double > diffVol1 ( - "diffVol1", - "Volume used to set diffusion scaling: firstVol[ voxel# ] " - "Particularly relevant for diffusion between PSD and head.", - &Dsolve::setDiffVol1, - &Dsolve::getDiffVol1 - ); - - static LookupValueFinfo< Dsolve, unsigned int, double > diffVol2 ( - "diffVol2", - "Volume used to set diffusion scaling: secondVol[ voxel# ] " - "Particularly relevant for diffusion between spine and dend.", - &Dsolve::setDiffVol2, - &Dsolve::getDiffVol2 - ); - - static LookupValueFinfo< Dsolve, unsigned int, double > diffScale ( - "diffScale", - "Geometry term to set diffusion scaling: diffScale[ voxel# ] " - "Here the scaling term is given by cross-section area/length " - "Relevant for diffusion between spine head and dend, or PSD.", - &Dsolve::setDiffScale, - &Dsolve::getDiffScale - ); - - - /////////////////////////////////////////////////////// - // DestFinfo definitions - /////////////////////////////////////////////////////// - - static DestFinfo process( "process", - "Handles process call", - new ProcOpFunc< Dsolve >( &Dsolve::process ) ); - static DestFinfo reinit( "reinit", - "Handles reinit call", - new ProcOpFunc< Dsolve >( &Dsolve::reinit ) ); - - static DestFinfo buildMeshJunctions( "buildMeshJunctions", - "Builds junctions between mesh on current Dsolve, and another" - " Dsolve. The meshes have to be compatible. ", - new EpFunc1< Dsolve, Id >( - &Dsolve::buildMeshJunctions ) ); - - static DestFinfo buildNeuroMeshJunctions( "buildNeuroMeshJunctions", - "Builds junctions between NeuroMesh, SpineMesh and PsdMesh", - new EpFunc2< Dsolve, Id, Id >( - &Dsolve::buildNeuroMeshJunctions ) ); - - /////////////////////////////////////////////////////// - // Shared definitions - /////////////////////////////////////////////////////// - static Finfo* procShared[] = { - &process, &reinit - }; - static SharedFinfo proc( "proc", - "Shared message for process and reinit", - procShared, sizeof( procShared ) / sizeof( const Finfo* ) - ); - - static Finfo* dsolveFinfos[] = - { - &stoich, // ElementValue - &path, // ElementValue - &compartment, // Value - &numVoxels, // ReadOnlyValue - &numAllVoxels, // ReadOnlyValue - &nVec, // LookupValue - &numPools, // Value - &diffVol1, // LookupValue - &diffVol2, // LookupValue - &diffScale, // LookupValue - &buildMeshJunctions, // DestFinfo - &buildNeuroMeshJunctions, // DestFinfo - &proc, // SharedFinfo - }; - - static Dinfo< Dsolve > dinfo; - static Cinfo dsolveCinfo( - "Dsolve", - Neutral::initCinfo(), - dsolveFinfos, - sizeof(dsolveFinfos)/sizeof(Finfo *), - &dinfo - ); - - return &dsolveCinfo; + /////////////////////////////////////////////////////// + // Field definitions + /////////////////////////////////////////////////////// + + static ValueFinfo< Dsolve, Id > stoich ( + "stoich", + "Stoichiometry object for handling this reaction system.", + &Dsolve::setStoich, + &Dsolve::getStoich + ); + + static ElementValueFinfo< Dsolve, string > path ( + "path", + "Path of reaction system. Must include all the pools that " + "are to be handled by the Dsolve, can also include other " + "random objects, which will be ignored.", + &Dsolve::setPath, + &Dsolve::getPath + ); + + static ReadOnlyValueFinfo< Dsolve, unsigned int > numVoxels( + "numVoxels", + "Number of voxels in the core reac-diff system, on the " + "current diffusion solver. ", + &Dsolve::getNumVoxels + ); + static ReadOnlyValueFinfo< Dsolve, unsigned int > numAllVoxels( + "numAllVoxels", + "Number of voxels in the core reac-diff system, on the " + "current diffusion solver. ", + &Dsolve::getNumVoxels + ); + static LookupValueFinfo< + Dsolve, unsigned int, vector< double > > nVec( + "nVec", + "vector of # of molecules along diffusion length, " + "looked up by pool index", + &Dsolve::setNvec, + &Dsolve::getNvec + ); + + static ValueFinfo< Dsolve, unsigned int > numPools( + "numPools", + "Number of molecular pools in the entire reac-diff system, " + "including variable, function and buffered.", + &Dsolve::setNumPools, + &Dsolve::getNumPools + ); + + static ValueFinfo< Dsolve, Id > compartment ( + "compartment", + "Reac-diff compartment in which this diffusion system is " + "embedded.", + &Dsolve::setCompartment, + &Dsolve::getCompartment + ); + + static LookupValueFinfo< Dsolve, unsigned int, double > diffVol1 ( + "diffVol1", + "Volume used to set diffusion scaling: firstVol[ voxel# ] " + "Particularly relevant for diffusion between PSD and head.", + &Dsolve::setDiffVol1, + &Dsolve::getDiffVol1 + ); + + static LookupValueFinfo< Dsolve, unsigned int, double > diffVol2 ( + "diffVol2", + "Volume used to set diffusion scaling: secondVol[ voxel# ] " + "Particularly relevant for diffusion between spine and dend.", + &Dsolve::setDiffVol2, + &Dsolve::getDiffVol2 + ); + + static LookupValueFinfo< Dsolve, unsigned int, double > diffScale ( + "diffScale", + "Geometry term to set diffusion scaling: diffScale[ voxel# ] " + "Here the scaling term is given by cross-section area/length " + "Relevant for diffusion between spine head and dend, or PSD.", + &Dsolve::setDiffScale, + &Dsolve::getDiffScale + ); + + + /////////////////////////////////////////////////////// + // DestFinfo definitions + /////////////////////////////////////////////////////// + + static DestFinfo process( "process", + "Handles process call", + new ProcOpFunc< Dsolve >( &Dsolve::process ) ); + static DestFinfo reinit( "reinit", + "Handles reinit call", + new ProcOpFunc< Dsolve >( &Dsolve::reinit ) ); + + static DestFinfo buildMeshJunctions( "buildMeshJunctions", + "Builds junctions between mesh on current Dsolve, and another" + " Dsolve. The meshes have to be compatible. ", + new EpFunc1< Dsolve, Id >( + &Dsolve::buildMeshJunctions ) ); + + static DestFinfo buildNeuroMeshJunctions( "buildNeuroMeshJunctions", + "Builds junctions between NeuroMesh, SpineMesh and PsdMesh", + new EpFunc2< Dsolve, Id, Id >( + &Dsolve::buildNeuroMeshJunctions ) ); + + /////////////////////////////////////////////////////// + // Shared definitions + /////////////////////////////////////////////////////// + static Finfo* procShared[] = + { + &process, &reinit + }; + static SharedFinfo proc( "proc", + "Shared message for process and reinit", + procShared, sizeof( procShared ) / sizeof( const Finfo* ) + ); + + static Finfo* dsolveFinfos[] = + { + &stoich, // ElementValue + &path, // ElementValue + &compartment, // Value + &numVoxels, // ReadOnlyValue + &numAllVoxels, // ReadOnlyValue + &nVec, // LookupValue + &numPools, // Value + &diffVol1, // LookupValue + &diffVol2, // LookupValue + &diffScale, // LookupValue + &buildMeshJunctions, // DestFinfo + &buildNeuroMeshJunctions, // DestFinfo + &proc, // SharedFinfo + }; + + static Dinfo< Dsolve > dinfo; + static Cinfo dsolveCinfo( + "Dsolve", + Neutral::initCinfo(), + dsolveFinfos, + sizeof(dsolveFinfos)/sizeof(Finfo *), + &dinfo + ); + + return &dsolveCinfo; } static const Cinfo* dsolveCinfo = Dsolve::initCinfo(); @@ -186,12 +189,12 @@ static const Cinfo* dsolveCinfo = Dsolve::initCinfo(); // Class definitions ////////////////////////////////////////////////////////////// Dsolve::Dsolve() - : - dt_( -1.0 ), - numTotPools_( 0 ), - numLocalPools_( 0 ), - poolStartIndex_( 0 ), - numVoxels_( 0 ) + : + dt_( -1.0 ), + numTotPools_( 0 ), + numLocalPools_( 0 ), + poolStartIndex_( 0 ), + numVoxels_( 0 ) {;} Dsolve::~Dsolve() @@ -203,89 +206,101 @@ Dsolve::~Dsolve() void Dsolve::setNvec( unsigned int pool, vector< double > vec ) { - if ( pool < pools_.size() ) { - if ( vec.size() != pools_[pool].getNumVoxels() ) { - cout << "Warning: Dsolve::setNvec: pool index out of range\n"; - } else { - pools_[ pool ].setNvec( vec ); - } - } + if ( pool < pools_.size() ) + { + if ( vec.size() != pools_[pool].getNumVoxels() ) + { + cout << "Warning: Dsolve::setNvec: pool index out of range\n"; + } + else + { + pools_[ pool ].setNvec( vec ); + } + } } vector< double > Dsolve::getNvec( unsigned int pool ) const { - static vector< double > ret; - if ( pool < pools_.size() ) - return pools_[pool].getNvec(); + static vector< double > ret; + if ( pool < pools_.size() ) + return pools_[pool].getNvec(); - cout << "Warning: Dsolve::setNvec: pool index out of range\n"; - return ret; + cout << "Warning: Dsolve::setNvec: pool index out of range\n"; + return ret; } static bool checkJn( const vector< DiffJunction >& jn, unsigned int voxel, - const string& info ) + const string& info ) { - if ( jn.size() < 1 ) { - cout << "Warning: Dsolve::" << info << ": junctions not defined.\n"; - return false; - } - if ( jn[0].vj.size() < voxel + 1 ) { - cout << "Warning: Dsolve:: " << info << ": " << voxel << - "out of range.\n"; - return false; - } - return true; + if ( jn.size() < 1 ) + { + cout << "Warning: Dsolve::" << info << ": junctions not defined.\n"; + return false; + } + if ( jn[0].vj.size() < voxel + 1 ) + { + cout << "Warning: Dsolve:: " << info << ": " << voxel << + "out of range.\n"; + return false; + } + return true; } void Dsolve::setDiffVol1( unsigned int voxel, double vol ) { - if ( checkJn( junctions_, voxel, "setDiffVol1" ) ) { - VoxelJunction& vj = junctions_[0].vj[ voxel ]; - vj.firstVol = vol; - } + if ( checkJn( junctions_, voxel, "setDiffVol1" ) ) + { + VoxelJunction& vj = junctions_[0].vj[ voxel ]; + vj.firstVol = vol; + } } double Dsolve::getDiffVol1( unsigned int voxel ) const { - if ( checkJn( junctions_, voxel, "getDiffVol1" ) ) { - const VoxelJunction& vj = junctions_[0].vj[ voxel ]; - return vj.firstVol; - } - return 0.0; + if ( checkJn( junctions_, voxel, "getDiffVol1" ) ) + { + const VoxelJunction& vj = junctions_[0].vj[ voxel ]; + return vj.firstVol; + } + return 0.0; } void Dsolve::setDiffVol2( unsigned int voxel, double vol ) { - if ( checkJn( junctions_, voxel, "setDiffVol2" ) ) { - VoxelJunction& vj = junctions_[0].vj[ voxel ]; - vj.secondVol = vol; - } + if ( checkJn( junctions_, voxel, "setDiffVol2" ) ) + { + VoxelJunction& vj = junctions_[0].vj[ voxel ]; + vj.secondVol = vol; + } } double Dsolve::getDiffVol2( unsigned int voxel ) const { - if ( checkJn( junctions_, voxel, "getDiffVol2" ) ) { - const VoxelJunction& vj = junctions_[0].vj[ voxel ]; - return vj.secondVol; - } - return 0.0; + if ( checkJn( junctions_, voxel, "getDiffVol2" ) ) + { + const VoxelJunction& vj = junctions_[0].vj[ voxel ]; + return vj.secondVol; + } + return 0.0; } void Dsolve::setDiffScale( unsigned int voxel, double adx ) { - if ( checkJn( junctions_, voxel, "setDiffScale" ) ) { - VoxelJunction& vj = junctions_[0].vj[ voxel ]; - vj.diffScale = adx; - } + if ( checkJn( junctions_, voxel, "setDiffScale" ) ) + { + VoxelJunction& vj = junctions_[0].vj[ voxel ]; + vj.diffScale = adx; + } } double Dsolve::getDiffScale( unsigned int voxel ) const { - if ( checkJn( junctions_, voxel, "getDiffScale" ) ) { - const VoxelJunction& vj = junctions_[0].vj[ voxel ]; - return vj.diffScale; - } - return 0.0; + if ( checkJn( junctions_, voxel, "getDiffScale" ) ) + { + const VoxelJunction& vj = junctions_[0].vj[ voxel ]; + return vj.diffScale; + } + return 0.0; } ////////////////////////////////////////////////////////////// @@ -294,155 +309,169 @@ double Dsolve::getDiffScale( unsigned int voxel ) const static double integ( double myN, double rf, double rb, double dt ) { - const double EPSILON = 1e-12; - if ( myN > EPSILON && rf > EPSILON ) { - double C = exp( -rf * dt / myN ); - myN *= C + ( rb / rf ) * ( 1.0 - C ); - } else { - myN += ( rb - rf ) * dt; - } - if ( myN < 0.0 ) - return 0.0; - return myN; + const double EPSILON = 1e-12; + if ( myN > EPSILON && rf > EPSILON ) + { + double C = exp( -rf * dt / myN ); + myN *= C + ( rb / rf ) * ( 1.0 - C ); + } + else + { + myN += ( rb - rf ) * dt; + } + if ( myN < 0.0 ) + return 0.0; + return myN; } void Dsolve::calcJnDiff( const DiffJunction& jn, Dsolve* other, double dt) { - const double EPSILON = 1e-16; - assert( jn.otherPools.size() == jn.myPools.size() ); - for ( unsigned int i = 0; i < jn.myPools.size(); ++i ) { - DiffPoolVec& myDv = pools_[ jn.myPools[i] ]; - if ( myDv.getDiffConst() < EPSILON ) - continue; - DiffPoolVec& otherDv = other->pools_[ jn.otherPools[i] ]; - if ( otherDv.getDiffConst() < EPSILON ) - continue; - // This geom mean is used in case we have the odd situation of - // different diffusion constants. - double effectiveDiffConst = - sqrt( myDv.getDiffConst() * otherDv.getDiffConst() ); - for ( vector< VoxelJunction >::const_iterator - j = jn.vj.begin(); j != jn.vj.end(); ++j ) { - double myN = myDv.getN( j->first ); - double otherN = otherDv.getN( j->second ); - // Here we do an exp Euler calculation - // rf is rate from self to other. - // double k = myDv.getDiffConst() * j->diffScale; - double k = effectiveDiffConst * j->diffScale; - double lastN = myN; - myN = integ( myN, - k * myN / j->firstVol, - k * otherN / j->secondVol, - dt - ); - otherN += lastN - myN; // Simple mass conservation - if ( otherN < 0.0 ) { // Avoid negatives - myN += otherN; - otherN = 0.0; - } - myDv.setN( j->first, myN ); - otherDv.setN( j->second, otherN ); - } - } + const double EPSILON = 1e-16; + assert( jn.otherPools.size() == jn.myPools.size() ); + for ( unsigned int i = 0; i < jn.myPools.size(); ++i ) + { + DiffPoolVec& myDv = pools_[ jn.myPools[i] ]; + if ( myDv.getDiffConst() < EPSILON ) + continue; + DiffPoolVec& otherDv = other->pools_[ jn.otherPools[i] ]; + if ( otherDv.getDiffConst() < EPSILON ) + continue; + // This geom mean is used in case we have the odd situation of + // different diffusion constants. + double effectiveDiffConst = + sqrt( myDv.getDiffConst() * otherDv.getDiffConst() ); + for ( vector< VoxelJunction >::const_iterator + j = jn.vj.begin(); j != jn.vj.end(); ++j ) + { + double myN = myDv.getN( j->first ); + double otherN = otherDv.getN( j->second ); + // Here we do an exp Euler calculation + // rf is rate from self to other. + // double k = myDv.getDiffConst() * j->diffScale; + double k = effectiveDiffConst * j->diffScale; + double lastN = myN; + myN = integ( myN, + k * myN / j->firstVol, + k * otherN / j->secondVol, + dt + ); + otherN += lastN - myN; // Simple mass conservation + if ( otherN < 0.0 ) // Avoid negatives + { + myN += otherN; + otherN = 0.0; + } + myDv.setN( j->first, myN ); + otherDv.setN( j->second, otherN ); + } + } } -void Dsolve::calcJnXfer( const DiffJunction& jn, - const vector< unsigned int >& srcXfer, - const vector< unsigned int >& destXfer, - Dsolve* srcDsolve, Dsolve* destDsolve ) +void Dsolve::calcJnXfer( const DiffJunction& jn, + const vector< unsigned int >& srcXfer, + const vector< unsigned int >& destXfer, + Dsolve* srcDsolve, Dsolve* destDsolve ) { - assert( destXfer.size() == srcXfer.size() ); - for ( unsigned int i = 0; i < srcXfer.size(); ++i ) { - DiffPoolVec& srcDv = srcDsolve->pools_[ srcXfer[i] ]; - DiffPoolVec& destDv = destDsolve->pools_[ destXfer[i] ]; - for ( vector< VoxelJunction >::const_iterator - j = jn.vj.begin(); j != jn.vj.end(); ++j ) { - double prevSrc = srcDv.getPrev( j->first ); - double prevDest = destDv.getPrev( j->second ); - double srcN = srcDv.getN( j->first ); - double destN = destDv.getN( j->second ); - // Consider delta as sum of local dN, and reference as prevDest - // newN = (srcN - prevSrc + destN - prevDest) + prevDest - double newN = srcN + destN - prevSrc; - srcDv.setN( j->first, newN ); - destDv.setN( j->second, newN ); - } - } + assert( destXfer.size() == srcXfer.size() ); + for ( unsigned int i = 0; i < srcXfer.size(); ++i ) + { + DiffPoolVec& srcDv = srcDsolve->pools_[ srcXfer[i] ]; + DiffPoolVec& destDv = destDsolve->pools_[ destXfer[i] ]; + for ( vector< VoxelJunction >::const_iterator + j = jn.vj.begin(); j != jn.vj.end(); ++j ) + { + double prevSrc = srcDv.getPrev( j->first ); + double prevDest = destDv.getPrev( j->second ); + double srcN = srcDv.getN( j->first ); + double destN = destDv.getN( j->second ); + // Consider delta as sum of local dN, and reference as prevDest + // newN = (srcN - prevSrc + destN - prevDest) + prevDest + double newN = srcN + destN - prevSrc; + srcDv.setN( j->first, newN ); + destDv.setN( j->second, newN ); + } + } } void Dsolve::calcJnChan( const DiffJunction& jn, Dsolve* other, double dt ) { - // Each jn has some channels - // Each channel has a chanPool, an intPool and an extPool. - // chanPool and intPool must be on self, extPool is on other. In - // cases where the intPool is on other, it attempts to swap the - // int and ext pools, but this too could fail - // because the chanPool could be a third compartment, such as the memb - // - // Don't have a solution for this case as yet. - // Other alternative is to have a message to update the N of the chan, - // so it isn't in the domain of the solver at all except for here. - // In which case we will want to point to the Moose object for it. - // - - for ( unsigned int i = 0; i < jn.myChannels.size(); ++i ) { - ConcChanInfo& myChan = channels_[ jn.myChannels[i] ]; - DiffPoolVec& myDv = pools_[ myChan.myPool ]; - DiffPoolVec& otherDv = other->pools_[ myChan.otherPool ]; - DiffPoolVec& chanDv = pools_[ myChan.chanPool ]; - for ( vector< VoxelJunction >::const_iterator - j = jn.vj.begin(); j != jn.vj.end(); ++j ) { - - double myN = myDv.getN( j->first ); - double lastN = myN; - double otherN = otherDv.getN( j->second ); - double chanN = chanDv.getN( j->first ); - double perm = myChan.permeability * chanN / NA; - myN = integ( myN, perm * myN/j->firstVol, - perm * otherN/j->secondVol, dt ); - otherN += lastN - myN; // Mass consv - if ( otherN < 0.0 ) { // Avoid negatives - myN += otherN; - otherN = 0.0; - } - myDv.setN( j->first, myN ); - otherDv.setN( j->second, otherN ); - } - } + // Each jn has some channels + // Each channel has a chanPool, an intPool and an extPool. + // chanPool and intPool must be on self, extPool is on other. In + // cases where the intPool is on other, it attempts to swap the + // int and ext pools, but this too could fail + // because the chanPool could be a third compartment, such as the memb + // + // Don't have a solution for this case as yet. + // Other alternative is to have a message to update the N of the chan, + // so it isn't in the domain of the solver at all except for here. + // In which case we will want to point to the Moose object for it. + // + + for ( unsigned int i = 0; i < jn.myChannels.size(); ++i ) + { + ConcChanInfo& myChan = channels_[ jn.myChannels[i] ]; + DiffPoolVec& myDv = pools_[ myChan.myPool ]; + DiffPoolVec& otherDv = other->pools_[ myChan.otherPool ]; + DiffPoolVec& chanDv = pools_[ myChan.chanPool ]; + for ( vector< VoxelJunction >::const_iterator + j = jn.vj.begin(); j != jn.vj.end(); ++j ) + { + + double myN = myDv.getN( j->first ); + double lastN = myN; + double otherN = otherDv.getN( j->second ); + double chanN = chanDv.getN( j->first ); + double perm = myChan.permeability * chanN / NA; + myN = integ( myN, perm * myN/j->firstVol, + perm * otherN/j->secondVol, dt ); + otherN += lastN - myN; // Mass consv + if ( otherN < 0.0 ) // Avoid negatives + { + myN += otherN; + otherN = 0.0; + } + myDv.setN( j->first, myN ); + otherDv.setN( j->second, otherN ); + } + } } // Same as above, but now go through channels on other Dsolve. void Dsolve::calcOtherJnChan( const DiffJunction& jn, Dsolve* other, double dt ) { - for ( unsigned int i = 0; i < jn.otherChannels.size(); ++i ) { - ConcChanInfo& otherChan = other->channels_[ jn.otherChannels[i] ]; - // This is the DiffPoolVec for the pools on the other Dsolve, - // the one with the channel. - // DiffPoolVec& otherDv = other->pools_[ jn.otherPools[otherChan.myPool] ]; - DiffPoolVec& otherDv = other->pools_[ otherChan.myPool ]; - // Local diffPoolVec. - // DiffPoolVec& myDv = pools_[ jn.myPools[otherChan.otherPool] ]; - DiffPoolVec& myDv = pools_[ otherChan.otherPool ]; - DiffPoolVec& chanDv = other->pools_[ otherChan.chanPool ]; - for ( vector< VoxelJunction >::const_iterator - j = jn.vj.begin(); j != jn.vj.end(); ++j ) { - - double myN = myDv.getN( j->first ); - double lastN = myN; - double otherN = otherDv.getN( j->second ); - double chanN = chanDv.getN( j->second ); - double perm = otherChan.permeability * chanN / NA; - myN = integ( myN, perm * myN/j->firstVol, - perm * otherN/j->secondVol, dt ); - otherN += lastN - myN; // Mass consv - if ( otherN < 0.0 ) { // Avoid negatives - myN += otherN; - otherN = 0.0; - } - myDv.setN( j->first, myN ); - otherDv.setN( j->second, otherN ); - } - } + for ( unsigned int i = 0; i < jn.otherChannels.size(); ++i ) + { + ConcChanInfo& otherChan = other->channels_[ jn.otherChannels[i] ]; + // This is the DiffPoolVec for the pools on the other Dsolve, + // the one with the channel. + // DiffPoolVec& otherDv = other->pools_[ jn.otherPools[otherChan.myPool] ]; + DiffPoolVec& otherDv = other->pools_[ otherChan.myPool ]; + // Local diffPoolVec. + // DiffPoolVec& myDv = pools_[ jn.myPools[otherChan.otherPool] ]; + DiffPoolVec& myDv = pools_[ otherChan.otherPool ]; + DiffPoolVec& chanDv = other->pools_[ otherChan.chanPool ]; + for ( vector< VoxelJunction >::const_iterator + j = jn.vj.begin(); j != jn.vj.end(); ++j ) + { + + double myN = myDv.getN( j->first ); + double lastN = myN; + double otherN = otherDv.getN( j->second ); + double chanN = chanDv.getN( j->second ); + double perm = otherChan.permeability * chanN / NA; + myN = integ( myN, perm * myN/j->firstVol, + perm * otherN/j->secondVol, dt ); + otherN += lastN - myN; // Mass consv + if ( otherN < 0.0 ) // Avoid negatives + { + myN += otherN; + otherN = 0.0; + } + myDv.setN( j->first, myN ); + otherDv.setN( j->second, otherN ); + } + } } /** @@ -456,145 +485,159 @@ void Dsolve::calcOtherJnChan( const DiffJunction& jn, Dsolve* other, double dt ) */ void Dsolve::calcJunction( const DiffJunction& jn, double dt ) { - Id oid( jn.otherDsolve ); - assert ( oid != Id() ); - assert ( oid.element()->cinfo()->isA( "Dsolve" ) ); + Id oid( jn.otherDsolve ); + assert ( oid != Id() ); + assert ( oid.element()->cinfo()->isA( "Dsolve" ) ); - Dsolve* other = reinterpret_cast< Dsolve* >( oid.eref().data() ); - calcJnDiff( jn, other, dt/2.0 ); + Dsolve* other = reinterpret_cast< Dsolve* >( oid.eref().data() ); + calcJnDiff( jn, other, dt/2.0 ); - calcJnChan( jn, other, dt/2.0 ); - calcOtherJnChan( jn, other, dt/2.0 ); + calcJnChan( jn, other, dt/2.0 ); + calcOtherJnChan( jn, other, dt/2.0 ); - calcJnXfer( jn, jn.myXferSrc, jn.otherXferDest, this, other ); - calcJnXfer( jn, jn.otherXferSrc, jn.myXferDest, other, this ); + calcJnXfer( jn, jn.myXferSrc, jn.otherXferDest, this, other ); + calcJnXfer( jn, jn.otherXferSrc, jn.myXferDest, other, this ); - calcJnDiff( jn, other, dt/2.0 ); + calcJnDiff( jn, other, dt/2.0 ); - calcJnChan( jn, other, dt/2.0 ); - calcOtherJnChan( jn, other, dt/2.0 ); + calcJnChan( jn, other, dt/2.0 ); + calcOtherJnChan( jn, other, dt/2.0 ); } void Dsolve::process( const Eref& e, ProcPtr p ) { - for ( vector< DiffPoolVec >::iterator - i = pools_.begin(); i != pools_.end(); ++i ) { - i->advance( p->dt ); - } + for ( auto i = pools_.begin(); i != pools_.end(); ++i ) + i->advance( p->dt ); } void Dsolve::reinit( const Eref& e, ProcPtr p ) { - build( p->dt ); - for ( vector< DiffPoolVec >::iterator - i = pools_.begin(); i != pools_.end(); ++i ) { - i->reinit(); - } + build( p->dt ); + for ( vector< DiffPoolVec >::iterator + i = pools_.begin(); i != pools_.end(); ++i ) + { + i->reinit(); + } } void Dsolve::updateJunctions( double dt ) { - for ( vector< DiffJunction >::const_iterator - i = junctions_.begin(); i != junctions_.end(); ++i ) { - calcJunction( *i, dt ); - } + for (auto i = junctions_.begin(); i != junctions_.end(); ++i ) + calcJunction( *i, dt ); } + + +void Dsolve::calcJunction_chunk( const size_t begin, const size_t end, double dt ) +{ + for (size_t i = begin; i < min(end, junctions_.size()); i++) + calcJunction( junctions_[i], dt ); + +} + + ////////////////////////////////////////////////////////////// // Solver coordination and setup functions ////////////////////////////////////////////////////////////// void Dsolve::setStoich( Id id ) { - if ( !id.element()->cinfo()->isA( "Stoich" ) ) { - cout << "Dsolve::setStoich::( " << id << " ): Error: provided Id is not a Stoich\n"; - return; - } - - stoich_ = id; - poolMap_ = Field< vector< unsigned int > >::get( stoich_, "poolIdMap" ); - poolMapStart_ = poolMap_.back(); - poolMap_.pop_back(); - - path_ = Field< string >::get( stoich_, "path" ); - // cout << "Pool Info for stoich " << id.path() << endl; - - for ( unsigned int i = 0; i < poolMap_.size(); ++i ) { - unsigned int poolIndex = poolMap_[i]; - if ( poolIndex != ~0U && poolIndex < pools_.size() ) { - // assert( poolIndex < pools_.size() ); - Id pid( i + poolMapStart_ ); - assert( pid.element()->cinfo()->isA( "PoolBase" ) ); - PoolBase* pb = - reinterpret_cast< PoolBase* >( pid.eref().data()); - double diffConst = pb->getDiffConst( pid.eref() ); - double motorConst = pb->getMotorConst( pid.eref() ); - pools_[ poolIndex ].setId( pid.value() ); - pools_[ poolIndex ].setDiffConst( diffConst ); - pools_[ poolIndex ].setMotorConst( motorConst ); - /* - cout << i << " poolIndex=" << poolIndex << - ", id=" << pid.value() << - ", name=" << pid.element()->getName() << endl; - */ - } - } - string chanpath = path_ + "[ISA=ConcChan]"; - vector< ObjId > chans; - wildcardFind( chanpath, chans ); - fillConcChans( chans ); + if ( !id.element()->cinfo()->isA( "Stoich" ) ) + { + cout << "Dsolve::setStoich::( " << id << " ): Error: provided Id is not a Stoich\n"; + return; + } + + stoich_ = id; + poolMap_ = Field< vector< unsigned int > >::get( stoich_, "poolIdMap" ); + poolMapStart_ = poolMap_.back(); + poolMap_.pop_back(); + + path_ = Field< string >::get( stoich_, "path" ); + // cout << "Pool Info for stoich " << id.path() << endl; + + for ( unsigned int i = 0; i < poolMap_.size(); ++i ) + { + unsigned int poolIndex = poolMap_[i]; + if ( poolIndex != ~0U && poolIndex < pools_.size() ) + { + // assert( poolIndex < pools_.size() ); + Id pid( i + poolMapStart_ ); + assert( pid.element()->cinfo()->isA( "PoolBase" ) ); + PoolBase* pb = + reinterpret_cast< PoolBase* >( pid.eref().data()); + double diffConst = pb->getDiffConst( pid.eref() ); + double motorConst = pb->getMotorConst( pid.eref() ); + pools_[ poolIndex ].setId( pid.value() ); + pools_[ poolIndex ].setDiffConst( diffConst ); + pools_[ poolIndex ].setMotorConst( motorConst ); + /* + cout << i << " poolIndex=" << poolIndex << + ", id=" << pid.value() << + ", name=" << pid.element()->getName() << endl; + */ + } + } + string chanpath = path_ + "[ISA=ConcChan]"; + vector< ObjId > chans; + wildcardFind( chanpath, chans ); + fillConcChans( chans ); } void Dsolve::fillConcChans( const vector< ObjId >& chans ) { - static const Cinfo* ccc = Cinfo::find( "ConcChan" ); - static const Finfo* inPoolFinfo = ccc->findFinfo( "inPool" ); - static const Finfo* outPoolFinfo = ccc->findFinfo( "outPool" ); - static const Finfo* chanPoolFinfo = ccc->findFinfo( "setNumChan" ); - FuncId fin = static_cast< const DestFinfo* >( inPoolFinfo )->getFid(); - FuncId fout = static_cast< const DestFinfo* >(outPoolFinfo )->getFid(); - FuncId fchan = - static_cast< const DestFinfo* >(chanPoolFinfo )->getFid(); - - // Find the in pools and the chan pools on the current compt. - // Save the Id of the outPool as an integer. - // Use these and the permeability to create the ConcChanInfo. - for ( auto i = chans.begin(); i != chans.end(); ++i ) { - vector< Id > ret; - if (i->element()->getNeighbors( ret, inPoolFinfo ) == 0 ) return; - ObjId inPool( ret[0] ); - ret.clear(); - if (i->element()->getNeighbors( ret, outPoolFinfo ) == 0 ) return; - ObjId outPool( ret[0] ); - ret.clear(); - if (i->element()->getNeighbors( ret, chanPoolFinfo ) == 0 ) return; - ObjId chanPool( ret[0] ); - ret.clear(); - unsigned int outPoolValue = outPool.id.value(); - bool swapped = false; - if ( !( inPool.bad() or chanPool.bad() ) ) { - unsigned int inPoolIndex = convertIdToPoolIndex( inPool.id ); - unsigned int chanPoolIndex = convertIdToPoolIndex(chanPool.id); - if ( inPoolIndex == ~0U ) { // Swap in and out as chan is symm - inPoolIndex = convertIdToPoolIndex( outPool.id ); - outPoolValue = inPool.id.value(); - swapped = true; - } - if ( ( inPoolIndex != ~0U) && (chanPoolIndex != ~0U ) ) { - ConcChanInfo cci( - inPoolIndex, outPoolValue, chanPoolIndex, - Field< double >::get( *i, "permeability" ), - ////// Fix it below //////// - swapped - ); - channels_.push_back( cci ); - } - } - } + static const Cinfo* ccc = Cinfo::find( "ConcChan" ); + static const Finfo* inPoolFinfo = ccc->findFinfo( "inPool" ); + static const Finfo* outPoolFinfo = ccc->findFinfo( "outPool" ); + static const Finfo* chanPoolFinfo = ccc->findFinfo( "setNumChan" ); + FuncId fin = static_cast< const DestFinfo* >( inPoolFinfo )->getFid(); + FuncId fout = static_cast< const DestFinfo* >(outPoolFinfo )->getFid(); + FuncId fchan = + static_cast< const DestFinfo* >(chanPoolFinfo )->getFid(); + + // Find the in pools and the chan pools on the current compt. + // Save the Id of the outPool as an integer. + // Use these and the permeability to create the ConcChanInfo. + for ( auto i = chans.begin(); i != chans.end(); ++i ) + { + vector< Id > ret; + if (i->element()->getNeighbors( ret, inPoolFinfo ) == 0 ) return; + ObjId inPool( ret[0] ); + ret.clear(); + if (i->element()->getNeighbors( ret, outPoolFinfo ) == 0 ) return; + ObjId outPool( ret[0] ); + ret.clear(); + if (i->element()->getNeighbors( ret, chanPoolFinfo ) == 0 ) return; + ObjId chanPool( ret[0] ); + ret.clear(); + unsigned int outPoolValue = outPool.id.value(); + bool swapped = false; + if ( !( inPool.bad() or chanPool.bad() ) ) + { + unsigned int inPoolIndex = convertIdToPoolIndex( inPool.id ); + unsigned int chanPoolIndex = convertIdToPoolIndex(chanPool.id); + if ( inPoolIndex == ~0U ) // Swap in and out as chan is symm + { + inPoolIndex = convertIdToPoolIndex( outPool.id ); + outPoolValue = inPool.id.value(); + swapped = true; + } + if ( ( inPoolIndex != ~0U) && (chanPoolIndex != ~0U ) ) + { + ConcChanInfo cci( + inPoolIndex, outPoolValue, chanPoolIndex, + Field< double >::get( *i, "permeability" ), + ////// Fix it below //////// + swapped + ); + channels_.push_back( cci ); + } + } + } } Id Dsolve::getStoich() const { - return stoich_; + return stoich_; } /// Inherited, defining dummy function here. @@ -603,100 +646,113 @@ void Dsolve::setDsolve( Id dsolve ) void Dsolve::setCompartment( Id id ) { - const Cinfo* c = id.element()->cinfo(); - compartment_ = id; - numVoxels_ = Field< unsigned int >::get( id, "numMesh" ); - if ( c->isA( "CubeMesh" ) ) { // we do only linear diffusion for now - unsigned int nx = Field< unsigned int >::get( id, "nx" ); - unsigned int ny = Field< unsigned int >::get( id, "nx" ); - unsigned int nz = Field< unsigned int >::get( id, "nx" ); - if ( !( nx*ny == 1 || nx*nz == 1 || ny*nz == 1 ) ) { - cout << "Warning: Dsolve::setCompartment:: Cube mesh: " << - c->name() << " found with >1 dimension of voxels. " << - "Only 1-D diffusion supported for now.\n"; - return; - } - } + const Cinfo* c = id.element()->cinfo(); + compartment_ = id; + numVoxels_ = Field< unsigned int >::get( id, "numMesh" ); + if ( c->isA( "CubeMesh" ) ) // we do only linear diffusion for now + { + unsigned int nx = Field< unsigned int >::get( id, "nx" ); + unsigned int ny = Field< unsigned int >::get( id, "nx" ); + unsigned int nz = Field< unsigned int >::get( id, "nx" ); + if ( !( nx*ny == 1 || nx*nz == 1 || ny*nz == 1 ) ) + { + cout << "Warning: Dsolve::setCompartment:: Cube mesh: " << + c->name() << " found with >1 dimension of voxels. " << + "Only 1-D diffusion supported for now.\n"; + return; + } + } } void Dsolve::makePoolMapFromElist( const vector< ObjId >& elist, - vector< Id >& temp ) + vector< Id >& temp ) { - unsigned int minId = 0; - unsigned int maxId = 0; - temp.resize( 0 ); - for ( vector< ObjId >::const_iterator - i = elist.begin(); i != elist.end(); ++i ) { - if ( i->element()->cinfo()->isA( "PoolBase" ) ) { - temp.push_back( i->id ); - if ( minId == 0 ) - maxId = minId = i->id.value(); - else if ( i->id.value() < minId ) - minId = i->id.value(); - else if ( i->id.value() > maxId ) - maxId = i->id.value(); - } - } - - if ( temp.size() == 0 ) { - cout << "Dsolve::makePoolMapFromElist::( " << path_ << - " ): Error: path is has no pools\n"; - return; - } - - stoich_ = Id(); - poolMapStart_ = minId; - poolMap_.resize( 1 + maxId - minId ); - for ( auto i = poolMap_.begin(); i != poolMap_.end(); ++i ) - *i = ~0U; - for ( unsigned int i = 0; i < temp.size(); ++i ) { - unsigned int idValue = temp[i].value(); - assert( idValue >= minId ); - assert( idValue - minId < poolMap_.size() ); - poolMap_[ idValue - minId ] = i; - } + unsigned int minId = 0; + unsigned int maxId = 0; + temp.resize( 0 ); + for ( vector< ObjId >::const_iterator + i = elist.begin(); i != elist.end(); ++i ) + { + if ( i->element()->cinfo()->isA( "PoolBase" ) ) + { + temp.push_back( i->id ); + if ( minId == 0 ) + maxId = minId = i->id.value(); + else if ( i->id.value() < minId ) + minId = i->id.value(); + else if ( i->id.value() > maxId ) + maxId = i->id.value(); + } + } + + if ( temp.size() == 0 ) + { + cout << "Dsolve::makePoolMapFromElist::( " << path_ << + " ): Error: path is has no pools\n"; + return; + } + + stoich_ = Id(); + poolMapStart_ = minId; + poolMap_.resize( 1 + maxId - minId ); + for ( auto i = poolMap_.begin(); i != poolMap_.end(); ++i ) + *i = ~0U; + for ( unsigned int i = 0; i < temp.size(); ++i ) + { + unsigned int idValue = temp[i].value(); + assert( idValue >= minId ); + assert( idValue - minId < poolMap_.size() ); + poolMap_[ idValue - minId ] = i; + } } void Dsolve::setPath( const Eref& e, string path ) { - vector< ObjId > elist; - simpleWildcardFind( path, elist ); - if ( elist.size() == 0 ) { - cout << "Dsolve::setPath::( " << path << " ): Error: path is empty\n"; - return; - } - vector< Id > temp; - makePoolMapFromElist( elist, temp ); - - setNumPools( temp.size() ); - for ( unsigned int i = 0; i < temp.size(); ++i ) { - Id id = temp[i]; - double diffConst = Field< double >::get( id, "diffConst" ); - double motorConst = Field< double >::get( id, "motorConst" ); - const Cinfo* c = id.element()->cinfo(); - if ( c == Pool::initCinfo() ) { - PoolBase::zombify( id.element(), ZombiePool::initCinfo(), Id(), e.id() ); - } else if ( c == BufPool::initCinfo() ) { - PoolBase::zombify( id.element(), ZombieBufPool::initCinfo(), Id(), e.id() ); - // Any Functions will have to continue to manage the BufPools. - // This needs them to be replicated, and for their messages - // to be copied over. Not really set up here. - } else { - cout << "Error: Dsolve::setPath( " << path << " ): unknown pool class:" << c->name() << endl; - } - id.element()->resize( numVoxels_ ); - - unsigned int j = temp[i].value() - poolMapStart_; - assert( j < poolMap_.size() ); - pools_[ poolMap_[i] ].setId( id.value() ); - pools_[ poolMap_[j] ].setDiffConst( diffConst ); - pools_[ poolMap_[j] ].setMotorConst( motorConst ); - } + vector< ObjId > elist; + simpleWildcardFind( path, elist ); + if ( elist.size() == 0 ) + { + cout << "Dsolve::setPath::( " << path << " ): Error: path is empty\n"; + return; + } + vector< Id > temp; + makePoolMapFromElist( elist, temp ); + + setNumPools( temp.size() ); + for ( unsigned int i = 0; i < temp.size(); ++i ) + { + Id id = temp[i]; + double diffConst = Field< double >::get( id, "diffConst" ); + double motorConst = Field< double >::get( id, "motorConst" ); + const Cinfo* c = id.element()->cinfo(); + if ( c == Pool::initCinfo() ) + { + PoolBase::zombify( id.element(), ZombiePool::initCinfo(), Id(), e.id() ); + } + else if ( c == BufPool::initCinfo() ) + { + PoolBase::zombify( id.element(), ZombieBufPool::initCinfo(), Id(), e.id() ); + // Any Functions will have to continue to manage the BufPools. + // This needs them to be replicated, and for their messages + // to be copied over. Not really set up here. + } + else + { + cout << "Error: Dsolve::setPath( " << path << " ): unknown pool class:" << c->name() << endl; + } + id.element()->resize( numVoxels_ ); + + unsigned int j = temp[i].value() - poolMapStart_; + assert( j < poolMap_.size() ); + pools_[ poolMap_[i] ].setId( id.value() ); + pools_[ poolMap_[j] ].setDiffConst( diffConst ); + pools_[ poolMap_[j] ].setMotorConst( motorConst ); + } } string Dsolve::getPath( const Eref& e ) const { - return path_; + return path_; } ///////////////////////////////////////////////////////////// @@ -722,43 +778,45 @@ string Dsolve::getPath( const Eref& e ) const void Dsolve::build( double dt ) { - if ( doubleEq( dt, dt_ ) ) - return; - if ( compartment_ == Id() ) { - cout << "Dsolve::build: Warning: No compartment defined. \n" - "Did you forget to assign 'stoich.dsolve = this' ?\n"; - return; - } - dt_ = dt; - const MeshCompt* m = reinterpret_cast< const MeshCompt* >( - compartment_.eref().data() ); - unsigned int numVoxels = m->getNumEntries(); - - for ( unsigned int i = 0; i < numLocalPools_; ++i ) { - bool debugFlag = false; - vector< unsigned int > diagIndex; - vector< double > diagVal; - vector< Triplet< double > > fops; - FastMatrixElim elim( numVoxels, numVoxels ); - if ( elim.buildForDiffusion( - m->getParentVoxel(), m->getVoxelVolume(), - m->getVoxelArea(), m->getVoxelLength(), - pools_[i].getDiffConst(), pools_[i].getMotorConst(), dt ) ) - { - vector< unsigned int > parentVoxel = m->getParentVoxel(); - assert( elim.checkSymmetricShape() ); - vector< unsigned int > lookupOldRowsFromNew; - elim.hinesReorder( parentVoxel, lookupOldRowsFromNew ); - assert( elim.checkSymmetricShape() ); - pools_[i].setNumVoxels( numVoxels_ ); - elim.buildForwardElim( diagIndex, fops ); - elim.buildBackwardSub( diagIndex, fops, diagVal ); - elim.opsReorder( lookupOldRowsFromNew, fops, diagVal ); - if (debugFlag ) - elim.print(); - } - pools_[i].setOps( fops, diagVal ); - } + if ( doubleEq( dt, dt_ ) ) + return; + if ( compartment_ == Id() ) + { + cout << "Dsolve::build: Warning: No compartment defined. \n" + "Did you forget to assign 'stoich.dsolve = this' ?\n"; + return; + } + dt_ = dt; + const MeshCompt* m = reinterpret_cast< const MeshCompt* >( + compartment_.eref().data() ); + unsigned int numVoxels = m->getNumEntries(); + + for ( unsigned int i = 0; i < numLocalPools_; ++i ) + { + bool debugFlag = false; + vector< unsigned int > diagIndex; + vector< double > diagVal; + vector< Triplet< double > > fops; + FastMatrixElim elim( numVoxels, numVoxels ); + if ( elim.buildForDiffusion( + m->getParentVoxel(), m->getVoxelVolume(), + m->getVoxelArea(), m->getVoxelLength(), + pools_[i].getDiffConst(), pools_[i].getMotorConst(), dt ) ) + { + vector< unsigned int > parentVoxel = m->getParentVoxel(); + assert( elim.checkSymmetricShape() ); + vector< unsigned int > lookupOldRowsFromNew; + elim.hinesReorder( parentVoxel, lookupOldRowsFromNew ); + assert( elim.checkSymmetricShape() ); + pools_[i].setNumVoxels( numVoxels_ ); + elim.buildForwardElim( diagIndex, fops ); + elim.buildBackwardSub( diagIndex, fops, diagVal ); + elim.opsReorder( lookupOldRowsFromNew, fops, diagVal ); + if (debugFlag ) + elim.print(); + } + pools_[i].setOps( fops, diagVal ); + } } /** @@ -767,195 +825,216 @@ void Dsolve::build( double dt ) // Would like to permit vectors of spines and psd compartments. void Dsolve::buildNeuroMeshJunctions( const Eref& e, Id spineD, Id psdD ) { - if ( !compartment_.element()->cinfo()->isA( "NeuroMesh" ) ) { - cout << "Warning: Dsolve::buildNeuroMeshJunction: Compartment '" << - compartment_.path() << "' is not a NeuroMesh\n"; - return; - } - Id spineMesh = Field< Id >::get( spineD, "compartment" ); - if ( !spineMesh.element()->cinfo()->isA( "SpineMesh" ) ) { - cout << "Warning: Dsolve::buildNeuroMeshJunction: Compartment '" << - spineMesh.path() << "' is not a SpineMesh\n"; - return; - } - Id psdMesh = Field< Id >::get( psdD, "compartment" ); - if ( !psdMesh.element()->cinfo()->isA( "PsdMesh" ) ) { - cout << "Warning: Dsolve::buildNeuroMeshJunction: Compartment '" << - psdMesh.path() << "' is not a PsdMesh\n"; - return; - } - - innerBuildMeshJunctions( spineD, e.id(), false ); - innerBuildMeshJunctions( psdD, spineD, false ); + if ( !compartment_.element()->cinfo()->isA( "NeuroMesh" ) ) + { + cout << "Warning: Dsolve::buildNeuroMeshJunction: Compartment '" << + compartment_.path() << "' is not a NeuroMesh\n"; + return; + } + Id spineMesh = Field< Id >::get( spineD, "compartment" ); + if ( !spineMesh.element()->cinfo()->isA( "SpineMesh" ) ) + { + cout << "Warning: Dsolve::buildNeuroMeshJunction: Compartment '" << + spineMesh.path() << "' is not a SpineMesh\n"; + return; + } + Id psdMesh = Field< Id >::get( psdD, "compartment" ); + if ( !psdMesh.element()->cinfo()->isA( "PsdMesh" ) ) + { + cout << "Warning: Dsolve::buildNeuroMeshJunction: Compartment '" << + psdMesh.path() << "' is not a PsdMesh\n"; + return; + } + + innerBuildMeshJunctions( spineD, e.id(), false ); + innerBuildMeshJunctions( psdD, spineD, false ); } void Dsolve::buildMeshJunctions( const Eref& e, Id other ) { - Id otherMesh; - if ( other.element()->cinfo()->isA( "Dsolve" ) ) { - otherMesh = Field< Id >::get( other, "compartment" ); - if ( compartment_.element()->cinfo()->isA( "ChemCompt" ) && - otherMesh.element()->cinfo()->isA( "ChemCompt" ) ) { - bool isMembraneBound = - Field< bool >::get( compartment_, "isMembraneBound" ); - innerBuildMeshJunctions( e.id(), other, isMembraneBound ); - return; - } - } - cout << "Warning: Dsolve::buildMeshJunctions: one of '" << - compartment_.path() << ", " << otherMesh.path() << - "' is not a Mesh\n"; + Id otherMesh; + if ( other.element()->cinfo()->isA( "Dsolve" ) ) + { + otherMesh = Field< Id >::get( other, "compartment" ); + if ( compartment_.element()->cinfo()->isA( "ChemCompt" ) && + otherMesh.element()->cinfo()->isA( "ChemCompt" ) ) + { + bool isMembraneBound = + Field< bool >::get( compartment_, "isMembraneBound" ); + innerBuildMeshJunctions( e.id(), other, isMembraneBound ); + return; + } + } + cout << "Warning: Dsolve::buildMeshJunctions: one of '" << + compartment_.path() << ", " << otherMesh.path() << + "' is not a Mesh\n"; } void printJunction( Id self, Id other, const DiffJunction& jn ) { - cout << "Junction between " << self.path() << ", " << other.path() << endl; - cout << "Pool indices: myPools, otherPools\n"; - for ( unsigned int i = 0; i < jn.myPools.size(); ++i ) - cout << i << " " << jn.myPools[i] << " " << jn.otherPools[i] << endl; - cout << "Voxel junctions: first second firstVol secondVol diffScale\n"; - for ( unsigned int i = 0; i < jn.vj.size(); ++i ) { - cout << i << " " << jn.vj[i].first << " " << jn.vj[i].second << - " " << jn.vj[i].firstVol << " " << jn.vj[i].secondVol << - " " << jn.vj[i].diffScale << endl; - } + cout << "Junction between " << self.path() << ", " << other.path() << endl; + cout << "Pool indices: myPools, otherPools\n"; + for ( unsigned int i = 0; i < jn.myPools.size(); ++i ) + cout << i << " " << jn.myPools[i] << " " << jn.otherPools[i] << endl; + cout << "Voxel junctions: first second firstVol secondVol diffScale\n"; + for ( unsigned int i = 0; i < jn.vj.size(); ++i ) + { + cout << i << " " << jn.vj[i].first << " " << jn.vj[i].second << + " " << jn.vj[i].firstVol << " " << jn.vj[i].secondVol << + " " << jn.vj[i].diffScale << endl; + } } -void Dsolve::mapDiffPoolsBetweenDsolves( DiffJunction& jn, - Id self, Id other) +void Dsolve::mapDiffPoolsBetweenDsolves( DiffJunction& jn, + Id self, Id other) { - Dsolve* mySolve = reinterpret_cast< Dsolve* >( self.eref().data() ); - unordered_map< string, unsigned int > myPools; - for ( unsigned int i = 0; i < mySolve->pools_.size(); ++i ) { - Id pool( mySolve->pools_[i].getId() ); - assert( pool != Id() ); - myPools[ pool.element()->getName() ] = i; - } - - const Dsolve* otherSolve = reinterpret_cast< const Dsolve* >( - other.eref().data() ); - for ( unsigned int i = 0; i < otherSolve->pools_.size(); ++i ) { - Id otherPool( otherSolve->pools_[i].getId() ); - unordered_map< string, unsigned int >::iterator p = - myPools.find( otherPool.element()->getName() ); - if ( p != myPools.end() ) { - jn.otherPools.push_back( i ); - jn.myPools.push_back( p->second ); - } - } + Dsolve* mySolve = reinterpret_cast< Dsolve* >( self.eref().data() ); + unordered_map< string, unsigned int > myPools; + for ( unsigned int i = 0; i < mySolve->pools_.size(); ++i ) + { + Id pool( mySolve->pools_[i].getId() ); + assert( pool != Id() ); + myPools[ pool.element()->getName() ] = i; + } + + const Dsolve* otherSolve = reinterpret_cast< const Dsolve* >( + other.eref().data() ); + for ( unsigned int i = 0; i < otherSolve->pools_.size(); ++i ) + { + Id otherPool( otherSolve->pools_[i].getId() ); + unordered_map< string, unsigned int >::iterator p = + myPools.find( otherPool.element()->getName() ); + if ( p != myPools.end() ) + { + jn.otherPools.push_back( i ); + jn.myPools.push_back( p->second ); + } + } } -/** +/** * static void mapXfersBetweenDsolves(...) * Build a list of all the molecules that should transfer instantaneously * to another compartment, for cross-compartment reactions. * Here we look for src names of the form <name>_xfer_<destComptName> - * For example, if we had an enzyme in compartment 'src', whose product - * should go to pool 'bar' in compartment 'dest', - * the name of the enzyme product in compartment src would be + * For example, if we had an enzyme in compartment 'src', whose product + * should go to pool 'bar' in compartment 'dest', + * the name of the enzyme product in compartment src would be * bar_xfer_dest */ void Dsolve::mapXfersBetweenDsolves( - vector< unsigned int >& srcPools, vector< unsigned int >& destPools, - Id src, Id dest ) + vector< unsigned int >& srcPools, vector< unsigned int >& destPools, + Id src, Id dest ) { - Id destMesh = Field< Id >::get( dest, "compartment" ); - string xferPost( string( "_xfer_" ) + destMesh.element()->getName() ); - size_t xlen = xferPost.length(); - - Dsolve* srcSolve = reinterpret_cast< Dsolve* >( src.eref().data() ); - unordered_map< string, unsigned int > srcMap; - for ( unsigned int i = 0; i < srcSolve->pools_.size(); ++i ) { - Id pool( srcSolve->pools_[i].getId() ); - assert( pool != Id() ); - string poolName = pool.element()->getName(); - if ( poolName.length() > xlen ) { - size_t prefixLen = poolName.length() - xlen; - if ( poolName.rfind( xferPost ) == prefixLen ) - srcMap[ poolName.substr( 0, prefixLen) ] = i; - } - } - - const Dsolve* destSolve = reinterpret_cast< const Dsolve* >( - dest.eref().data() ); - for ( unsigned int i = 0; i < destSolve->pools_.size(); ++i ) { - Id destPool( destSolve->pools_[i].getId() ); - unordered_map< string, unsigned int >::iterator p = - srcMap.find( destPool.element()->getName() ); - if ( p != srcMap.end() ) { - destPools.push_back( i ); - srcPools.push_back( p->second ); - } - } + Id destMesh = Field< Id >::get( dest, "compartment" ); + string xferPost( string( "_xfer_" ) + destMesh.element()->getName() ); + size_t xlen = xferPost.length(); + + Dsolve* srcSolve = reinterpret_cast< Dsolve* >( src.eref().data() ); + unordered_map< string, unsigned int > srcMap; + for ( unsigned int i = 0; i < srcSolve->pools_.size(); ++i ) + { + Id pool( srcSolve->pools_[i].getId() ); + assert( pool != Id() ); + string poolName = pool.element()->getName(); + if ( poolName.length() > xlen ) + { + size_t prefixLen = poolName.length() - xlen; + if ( poolName.rfind( xferPost ) == prefixLen ) + srcMap[ poolName.substr( 0, prefixLen) ] = i; + } + } + + const Dsolve* destSolve = reinterpret_cast< const Dsolve* >( + dest.eref().data() ); + for ( unsigned int i = 0; i < destSolve->pools_.size(); ++i ) + { + Id destPool( destSolve->pools_[i].getId() ); + unordered_map< string, unsigned int >::iterator p = + srcMap.find( destPool.element()->getName() ); + if ( p != srcMap.end() ) + { + destPools.push_back( i ); + srcPools.push_back( p->second ); + } + } } void Dsolve::mapChansBetweenDsolves( DiffJunction& jn, Id self, Id other) { - Dsolve* otherSolve = reinterpret_cast< Dsolve* >( - other.eref().data() ); - Dsolve* selfSolve = reinterpret_cast< Dsolve* >( self.eref().data() ); - vector< ConcChanInfo >& ch = selfSolve->channels_; - unsigned int outIndex; - for ( unsigned int i = 0; i < ch.size(); ++i ) { - unsigned int chanIndex = ch[i].chanPool; - outIndex = otherSolve->convertIdToPoolIndex( ch[i].otherPool ); - if ( (outIndex != ~0U) && (chanIndex != ~0U ) ) { - jn.myChannels.push_back(i); - ch[i].otherPool = outIndex; // replace the Id with the index. - ch[i].chanPool = chanIndex; //chanIndex may be on either Dsolve - } - } - // Now set up the other Dsolve. - vector< ConcChanInfo >& ch2 = otherSolve->channels_; - for ( unsigned int i = 0; i < ch2.size(); ++i ) { - unsigned int chanIndex = ch2[i].chanPool; - outIndex = selfSolve->convertIdToPoolIndex( ch2[i].otherPool ); - if ( (outIndex != ~0U) && (chanIndex != ~0U) ) { - jn.otherChannels.push_back(i); - ch2[i].otherPool = outIndex; // replace the Id with the index - ch2[i].chanPool = chanIndex; //chanIndex may be on either Dsolve - } - } + Dsolve* otherSolve = reinterpret_cast< Dsolve* >( + other.eref().data() ); + Dsolve* selfSolve = reinterpret_cast< Dsolve* >( self.eref().data() ); + vector< ConcChanInfo >& ch = selfSolve->channels_; + unsigned int outIndex; + for ( unsigned int i = 0; i < ch.size(); ++i ) + { + unsigned int chanIndex = ch[i].chanPool; + outIndex = otherSolve->convertIdToPoolIndex( ch[i].otherPool ); + if ( (outIndex != ~0U) && (chanIndex != ~0U ) ) + { + jn.myChannels.push_back(i); + ch[i].otherPool = outIndex; // replace the Id with the index. + ch[i].chanPool = chanIndex; //chanIndex may be on either Dsolve + } + } + // Now set up the other Dsolve. + vector< ConcChanInfo >& ch2 = otherSolve->channels_; + for ( unsigned int i = 0; i < ch2.size(); ++i ) + { + unsigned int chanIndex = ch2[i].chanPool; + outIndex = selfSolve->convertIdToPoolIndex( ch2[i].otherPool ); + if ( (outIndex != ~0U) && (chanIndex != ~0U) ) + { + jn.otherChannels.push_back(i); + ch2[i].otherPool = outIndex; // replace the Id with the index + ch2[i].chanPool = chanIndex; //chanIndex may be on either Dsolve + } + } } static void mapVoxelsBetweenMeshes( DiffJunction& jn, Id self, Id other) { - Id myMesh = Field< Id >::get( self, "compartment" ); - Id otherMesh = Field< Id >::get( other, "compartment" ); - - const ChemCompt* myCompt = reinterpret_cast< const ChemCompt* >( - myMesh.eref().data() ); - const ChemCompt* otherCompt = reinterpret_cast< const ChemCompt* >( - otherMesh.eref().data() ); - myCompt->matchMeshEntries( otherCompt, jn.vj ); - vector< double > myVols = myCompt->getVoxelVolume(); - vector< double > otherVols = otherCompt->getVoxelVolume(); - for ( vector< VoxelJunction >::iterator - i = jn.vj.begin(); i != jn.vj.end(); ++i ) { - i->firstVol = myVols[i->first]; - i->secondVol = otherVols[i->second]; - } + Id myMesh = Field< Id >::get( self, "compartment" ); + Id otherMesh = Field< Id >::get( other, "compartment" ); + + const ChemCompt* myCompt = reinterpret_cast< const ChemCompt* >( + myMesh.eref().data() ); + const ChemCompt* otherCompt = reinterpret_cast< const ChemCompt* >( + otherMesh.eref().data() ); + myCompt->matchMeshEntries( otherCompt, jn.vj ); + vector< double > myVols = myCompt->getVoxelVolume(); + vector< double > otherVols = otherCompt->getVoxelVolume(); + for ( vector< VoxelJunction >::iterator + i = jn.vj.begin(); i != jn.vj.end(); ++i ) + { + i->firstVol = myVols[i->first]; + i->secondVol = otherVols[i->second]; + } } // Static utility func for building junctions void Dsolve::innerBuildMeshJunctions( Id self, Id other, bool selfIsMembraneBound ) { - DiffJunction jn; // This is based on the Spine Dsolver. - jn.otherDsolve = other.value(); - Dsolve* dself = reinterpret_cast< Dsolve* >( self.eref().data() ); - if ( selfIsMembraneBound ) { - mapChansBetweenDsolves( jn, self, other ); - } else { - mapDiffPoolsBetweenDsolves( jn, self, other ); - } - mapXfersBetweenDsolves( jn.myXferSrc, jn.otherXferDest, self, other ); - mapXfersBetweenDsolves( jn.otherXferSrc, jn.myXferDest, other, self ); - - mapVoxelsBetweenMeshes( jn, self, other ); - - - // printJunction( self, other, jn ); - dself->junctions_.push_back( jn ); + DiffJunction jn; // This is based on the Spine Dsolver. + jn.otherDsolve = other.value(); + Dsolve* dself = reinterpret_cast< Dsolve* >( self.eref().data() ); + if ( selfIsMembraneBound ) + { + mapChansBetweenDsolves( jn, self, other ); + } + else + { + mapDiffPoolsBetweenDsolves( jn, self, other ); + } + mapXfersBetweenDsolves( jn.myXferSrc, jn.otherXferDest, self, other ); + mapXfersBetweenDsolves( jn.otherXferSrc, jn.myXferDest, other, self ); + + mapVoxelsBetweenMeshes( jn, self, other ); + + + // printJunction( self, other, jn ); + dself->junctions_.push_back( jn ); } ///////////////////////////////////////////////////////////// @@ -964,191 +1043,202 @@ void Dsolve::innerBuildMeshJunctions( Id self, Id other, bool selfIsMembraneBoun // unsigned int Dsolve::getNumVarPools() const { - return 0; + return 0; } unsigned int Dsolve::getNumVoxels() const { - return numVoxels_; + return numVoxels_; } void Dsolve::setNumAllVoxels( unsigned int num ) { - numVoxels_ = num; - for ( unsigned int i = 0 ; i < numLocalPools_; ++i ) - pools_[i].setNumVoxels( numVoxels_ ); + numVoxels_ = num; + for ( unsigned int i = 0 ; i < numLocalPools_; ++i ) + pools_[i].setNumVoxels( numVoxels_ ); } unsigned int Dsolve::convertIdToPoolIndex( const Id id ) const { - unsigned int i = id.value() - poolMapStart_; - if ( i < poolMap_.size() ) { - return poolMap_[i]; - } - cout << "Warning: Dsolve::convertIdToPoollndex: Id out of range, (" << - poolMapStart_ << ", " << id << ", " << id.path() << ", " << - poolMap_.size() + poolMapStart_ << "\n"; - return 0; + unsigned int i = id.value() - poolMapStart_; + if ( i < poolMap_.size() ) + { + return poolMap_[i]; + } + cout << "Warning: Dsolve::convertIdToPoollndex: Id out of range, (" << + poolMapStart_ << ", " << id << ", " << id.path() << ", " << + poolMap_.size() + poolMapStart_ << "\n"; + return 0; } unsigned int Dsolve::convertIdToPoolIndex( const Eref& e ) const { - return convertIdToPoolIndex( e.id() ); + return convertIdToPoolIndex( e.id() ); } void Dsolve::setN( const Eref& e, double v ) { - unsigned int pid = convertIdToPoolIndex( e ); - // Ignore silently, as this may be a valid pid for the ksolve to use. - if ( pid >= pools_.size() ) - return; - unsigned int vox = e.dataIndex(); - if ( vox < numVoxels_ ) { - pools_[ pid ].setN( vox, v ); - return; - } - cout << "Warning: Dsolve::setN: Eref " << e << " out of range " << - pools_.size() << ", " << numVoxels_ << "\n"; + unsigned int pid = convertIdToPoolIndex( e ); + // Ignore silently, as this may be a valid pid for the ksolve to use. + if ( pid >= pools_.size() ) + return; + unsigned int vox = e.dataIndex(); + if ( vox < numVoxels_ ) + { + pools_[ pid ].setN( vox, v ); + return; + } + cout << "Warning: Dsolve::setN: Eref " << e << " out of range " << + pools_.size() << ", " << numVoxels_ << "\n"; } double Dsolve::getN( const Eref& e ) const { - unsigned int pid = convertIdToPoolIndex( e ); - if ( pid >= pools_.size() ) return 0.0; // ignore silently - unsigned int vox = e.dataIndex(); - if ( vox < numVoxels_ ) { - return pools_[ pid ].getN( vox ); - } - cout << "Warning: Dsolve::setN: Eref " << e << " out of range " << - pools_.size() << ", " << numVoxels_ << "\n"; - return 0.0; + unsigned int pid = convertIdToPoolIndex( e ); + if ( pid >= pools_.size() ) return 0.0; // ignore silently + unsigned int vox = e.dataIndex(); + if ( vox < numVoxels_ ) + { + return pools_[ pid ].getN( vox ); + } + cout << "Warning: Dsolve::setN: Eref " << e << " out of range " << + pools_.size() << ", " << numVoxels_ << "\n"; + return 0.0; } void Dsolve::setNinit( const Eref& e, double v ) { - unsigned int pid = convertIdToPoolIndex( e ); - if ( pid >= pools_.size() ) // Ignore silently - return; - unsigned int vox = e.dataIndex(); - if ( vox < numVoxels_ ) { - pools_[ pid ].setNinit( vox, v ); - return; - } - cout << "Warning: Dsolve::setNinit: Eref " << e << " out of range " << - pools_.size() << ", " << numVoxels_ << "\n"; + unsigned int pid = convertIdToPoolIndex( e ); + if ( pid >= pools_.size() ) // Ignore silently + return; + unsigned int vox = e.dataIndex(); + if ( vox < numVoxels_ ) + { + pools_[ pid ].setNinit( vox, v ); + return; + } + cout << "Warning: Dsolve::setNinit: Eref " << e << " out of range " << + pools_.size() << ", " << numVoxels_ << "\n"; } double Dsolve::getNinit( const Eref& e ) const { - unsigned int pid = convertIdToPoolIndex( e ); - if ( pid >= pools_.size() ) return 0.0; // ignore silently - unsigned int vox = e.dataIndex(); - if ( vox < numVoxels_ ) { - return pools_[ pid ].getNinit( vox ); - } - cout << "Warning: Dsolve::setNinit: Eref " << e << " out of range " << - pools_.size() << ", " << numVoxels_ << "\n"; - return 0.0; + unsigned int pid = convertIdToPoolIndex( e ); + if ( pid >= pools_.size() ) return 0.0; // ignore silently + unsigned int vox = e.dataIndex(); + if ( vox < numVoxels_ ) + { + return pools_[ pid ].getNinit( vox ); + } + cout << "Warning: Dsolve::setNinit: Eref " << e << " out of range " << + pools_.size() << ", " << numVoxels_ << "\n"; + return 0.0; } void Dsolve::setDiffConst( const Eref& e, double v ) { - unsigned int pid = convertIdToPoolIndex( e ); - if ( pid >= pools_.size() ) // Ignore silently, out of range. - return; - pools_[ convertIdToPoolIndex( e ) ].setDiffConst( v ); + unsigned int pid = convertIdToPoolIndex( e ); + if ( pid >= pools_.size() ) // Ignore silently, out of range. + return; + pools_[ convertIdToPoolIndex( e ) ].setDiffConst( v ); } double Dsolve::getDiffConst( const Eref& e ) const { - unsigned int pid = convertIdToPoolIndex( e ); - if ( pid >= pools_.size() ) // Ignore silently, out of range. - return 0.0; - return pools_[ convertIdToPoolIndex( e ) ].getDiffConst(); + unsigned int pid = convertIdToPoolIndex( e ); + if ( pid >= pools_.size() ) // Ignore silently, out of range. + return 0.0; + return pools_[ convertIdToPoolIndex( e ) ].getDiffConst(); } void Dsolve::setMotorConst( const Eref& e, double v ) { - unsigned int pid = convertIdToPoolIndex( e ); - if ( pid >= pools_.size() ) // Ignore silently, out of range. - return; - pools_[ convertIdToPoolIndex( e ) ].setMotorConst( v ); + unsigned int pid = convertIdToPoolIndex( e ); + if ( pid >= pools_.size() ) // Ignore silently, out of range. + return; + pools_[ convertIdToPoolIndex( e ) ].setMotorConst( v ); } void Dsolve::setNumPools( unsigned int numPoolSpecies ) { - // Decompose numPoolSpecies here, assigning some to each node. - numTotPools_ = numPoolSpecies; - numLocalPools_ = numPoolSpecies; - poolStartIndex_ = 0; - - pools_.resize( numLocalPools_ ); - for ( unsigned int i = 0 ; i < numLocalPools_; ++i ) { - pools_[i].setNumVoxels( numVoxels_ ); - // pools_[i].setId( reversePoolMap_[i] ); - // pools_[i].setParent( me ); - } + // Decompose numPoolSpecies here, assigning some to each node. + numTotPools_ = numPoolSpecies; + numLocalPools_ = numPoolSpecies; + poolStartIndex_ = 0; + + pools_.resize( numLocalPools_ ); + for ( unsigned int i = 0 ; i < numLocalPools_; ++i ) + { + pools_[i].setNumVoxels( numVoxels_ ); + // pools_[i].setId( reversePoolMap_[i] ); + // pools_[i].setParent( me ); + } } unsigned int Dsolve::getNumPools() const { - return numTotPools_; + return numTotPools_; } // July 2014: This is half-baked wrt the startPool. void Dsolve::getBlock( vector< double >& values ) const { - unsigned int startVoxel = values[0]; - unsigned int numVoxels = values[1]; - unsigned int startPool = values[2]; - unsigned int numPools = values[3]; - - assert( startVoxel + numVoxels <= numVoxels_ ); - assert( startPool >= poolStartIndex_ ); - assert( numPools + startPool <= numLocalPools_ ); - values.resize( 4 ); - - for ( unsigned int i = 0; i < numPools; ++i ) { - unsigned int j = i + startPool; - if ( j >= poolStartIndex_ && j < poolStartIndex_ + numLocalPools_ ){ - vector< double >::const_iterator q = - pools_[ j - poolStartIndex_ ].getNvec().begin(); - - values.insert( values.end(), - q + startVoxel, q + startVoxel + numVoxels ); - } - } + unsigned int startVoxel = values[0]; + unsigned int numVoxels = values[1]; + unsigned int startPool = values[2]; + unsigned int numPools = values[3]; + + assert( startVoxel + numVoxels <= numVoxels_ ); + assert( startPool >= poolStartIndex_ ); + assert( numPools + startPool <= numLocalPools_ ); + values.resize( 4 ); + + for ( unsigned int i = 0; i < numPools; ++i ) + { + unsigned int j = i + startPool; + if ( j >= poolStartIndex_ && j < poolStartIndex_ + numLocalPools_ ) + { + vector< double >::const_iterator q = + pools_[ j - poolStartIndex_ ].getNvec().begin(); + + values.insert( values.end(), + q + startVoxel, q + startVoxel + numVoxels ); + } + } } // Inefficient but easy to set up. Optimize later. void Dsolve::setPrev() { - for ( auto i = pools_.begin(); i != pools_.end(); ++i ) { - // if (i->getDiffConst() > 0.0 ) - i->setPrevVec(); - } + for ( auto i = pools_.begin(); i != pools_.end(); ++i ) + { + // if (i->getDiffConst() > 0.0 ) + i->setPrevVec(); + } } void Dsolve::setBlock( const vector< double >& values ) { - unsigned int startVoxel = values[0]; - unsigned int numVoxels = values[1]; - unsigned int startPool = values[2]; - unsigned int numPools = values[3]; - - assert( startVoxel + numVoxels <= numVoxels_ ); - assert( startPool >= poolStartIndex_ ); - assert( numPools + startPool <= numLocalPools_ ); - assert( values.size() == 4 + numVoxels * numPools ); - - for ( unsigned int i = 0; i < numPools; ++i ) { - unsigned int j = i + startPool; - if ( j >= poolStartIndex_ && j < poolStartIndex_ + numLocalPools_ ){ - vector< double >::const_iterator - q = values.begin() + 4 + i * numVoxels; - pools_[ j - poolStartIndex_ ].setNvec( startVoxel, numVoxels, q ); - } - } + unsigned int startVoxel = values[0]; + unsigned int numVoxels = values[1]; + unsigned int startPool = values[2]; + unsigned int numPools = values[3]; + + assert( startVoxel + numVoxels <= numVoxels_ ); + assert( startPool >= poolStartIndex_ ); + assert( numPools + startPool <= numLocalPools_ ); + assert( values.size() == 4 + numVoxels * numPools ); + + for ( unsigned int i = 0; i < numPools; ++i ) + { + unsigned int j = i + startPool; + if ( j >= poolStartIndex_ && j < poolStartIndex_ + numLocalPools_ ) + { + vector< double >::const_iterator + q = values.begin() + 4 + i * numVoxels; + pools_[ j - poolStartIndex_ ].setNvec( startVoxel, numVoxels, q ); + } + } } ////////////////////////////////////////////////////////////////////// @@ -1156,25 +1246,25 @@ void Dsolve::setBlock( const vector< double >& values ) void Dsolve::updateRateTerms( unsigned int index ) { - ; + ; } unsigned int Dsolve::getPoolIndex( const Eref& e ) const { - return convertIdToPoolIndex( e ); + return convertIdToPoolIndex( e ); } unsigned int Dsolve::getNumLocalVoxels() const { - return numVoxels_; + return numVoxels_; } VoxelPoolsBase* Dsolve::pools( unsigned int i ) { - return 0; + return 0; } double Dsolve::volume( unsigned int i ) const { - return 1.0; + return 1.0; } diff --git a/diffusion/Dsolve.h b/diffusion/Dsolve.h index b8197ef4..dc013d3c 100644 --- a/diffusion/Dsolve.h +++ b/diffusion/Dsolve.h @@ -28,198 +28,202 @@ */ class Dsolve: public ZombiePoolInterface { - public: - Dsolve(); - ~Dsolve(); - - ////////////////////////////////////////////////////////////////// - // Field assignment stuff - ////////////////////////////////////////////////////////////////// - unsigned int getNumVarPools() const; - - void setStoich( Id id ); - Id getStoich() const; - void setCompartment( Id id ); - // Defined in base class. Id getCompartment() const; - void setDsolve( Id id ); /// Dummy, inherited but not used. - - void setPath( const Eref& e, string path ); - string getPath( const Eref& e ) const; - - unsigned int getNumVoxels() const; - /// Inherited virtual. - void setNumAllVoxels( unsigned int numVoxels ); - - vector< double > getNvec( unsigned int pool ) const; - void setNvec( unsigned int pool, vector< double > vec ); - - /// LookupFied for examining cross-solver diffusion terms. - double getDiffVol1( unsigned int voxel ) const; - void setDiffVol1( unsigned int voxel, double vol ); - double getDiffVol2( unsigned int voxel ) const; - void setDiffVol2( unsigned int voxel, double vol ); - double getDiffScale( unsigned int voxel ) const; - void setDiffScale( unsigned int voxel, double scale ); - - ////////////////////////////////////////////////////////////////// - // Dest Finfos - ////////////////////////////////////////////////////////////////// - void process( const Eref& e, ProcPtr p ); - void reinit( const Eref& e, ProcPtr p ); - - ////////////////////////////////////////////////////////////////// - void updateJunctions( double dt ); - - /** - * Builds junctions between Dsolves handling NeuroMesh, SpineMesh, - * and PsdMesh. Must only be called from the one handling the - * NeuroMesh. - * These junctions handle diffusion between different meshes. - * Note that a single NeuroMesh may contact many spines which are - * all in a single SpineMesh. Likewise each spine has a single - * PSD, but there are many spines in the SpineMesh and matching - * psds in the PsdMesh. Finally, note that - * there may be many molecules which diffuse across each diffusion - * junction. - */ - void buildNeuroMeshJunctions( const Eref& e, Id spineD, Id psdD ); - - /** - * Builds junctions between current Dsolve and another. For this - * to work the respective meshes must be compatible. - * These junctions handle diffusion between different meshes. - */ - void buildMeshJunctions( const Eref& e, Id other ); - - /** - * buildMeshJunctions is the inner utility function for building - * the junction between any specified pair of Dsolves. - * Note that it builds the junction on the 'self' Dsolve. - */ - static void innerBuildMeshJunctions( Id self, Id other, - bool isMembraneBound ); - - /// Sets up map of matching pools for diffusion. - static void mapDiffPoolsBetweenDsolves( DiffJunction& jn, - Id self, Id other); - - static void mapXfersBetweenDsolves( - vector< unsigned int >& srcPools, - vector< unsigned int >& destPools, - Id src, Id dest ); - static void mapChansBetweenDsolves( DiffJunction& jn, - Id self, Id other); - - /** - * Computes flux through a junction between diffusion solvers. - * Most used at junctions on spines and PSDs, but can also be used - * when a given diff solver is decomposed. At present the lookups - * on the other diffusion solver assume that the data is on the - * local node. Once this works well I can figure out how to do - * across nodes. - */ - void calcJunction( const DiffJunction& jn, double dt ); - ////////////////////////////////////////////////////////////////// - // Inherited virtual funcs from ZombiePoolInterface - ////////////////////////////////////////////////////////////////// - double getNinit( const Eref& e ) const; - void setNinit( const Eref& e, double value ); - double getN( const Eref& e ) const; - void setN( const Eref& e, double value ); - double getDiffConst( const Eref& e ) const; - void setDiffConst( const Eref& e, double value ); - void setMotorConst( const Eref& e, double value ); - - void setNumPools( unsigned int num ); - unsigned int getNumPools() const; - unsigned int getNumLocalVoxels() const; - VoxelPoolsBase* pools( unsigned int i ); - double volume( unsigned int i ) const; - - void getBlock( vector< double >& values ) const; - void setBlock( const vector< double >& values ); - void setPrev(); - - // This one isn't used in Dsolve, but is defined as a dummy. - void setupCrossSolverReacs( - const map< Id, vector< Id > >& xr, Id otherStoich ); - void setupCrossSolverReacVols( - const vector< vector< Id > >& subCompts, - const vector< vector< Id > >& prdCompts ); - void filterCrossRateTerms( const vector< pair< Id, Id > >& xrt ); - - // Do any updates following a volume or rate constant change. - void updateRateTerms( unsigned int index ); - ////////////////////////////////////////////////////////////////// - // Model traversal and building functions - ////////////////////////////////////////////////////////////////// - unsigned int convertIdToPoolIndex( Id id ) const; - unsigned int convertIdToPoolIndex( const Eref& e ) const; - unsigned int getPoolIndex( const Eref& e ) const; - - /** - * Fills in poolMap_ using elist of objects found when the - * 'setPath' function is executed. temp is returned with the - * list of PoolBase objects that exist on the path. - */ - void makePoolMapFromElist( const vector< ObjId >& elist, - vector< Id >& temp ); - - /** - * This key function does the work of setting up the Dsolve. - * Should be called after the compartment has been attached to - * the Dsolve, and the stoich is assigned. - * Called during the setStoich function. - */ - void build( double dt ); - void rebuildPools(); - void calcJnDiff( const DiffJunction& jn, Dsolve* other, double dt ); - void calcJnXfer( const DiffJunction& jn, - const vector< unsigned int >& srcXfer, - const vector< unsigned int >& destXfer, - Dsolve* srcDsolve, Dsolve* destDsolve ); - void calcJnChan( const DiffJunction& jn, Dsolve* other, double dt ); - void calcOtherJnChan( const DiffJunction& jn, Dsolve* other, - double dt ); - void fillConcChans( const vector< ObjId >& chans ); - - /** - * Utility func for debugging: Prints N_ matrix - */ - void print() const; - - ////////////////////////////////////////////////////////////////// - static const Cinfo* initCinfo(); - private: - - /// Path of pools managed by Dsolve, may include other classes too. - string path_; - - /// Timestep used by diffusion calculations. - double dt_; - - unsigned int numTotPools_; - unsigned int numLocalPools_; - unsigned int poolStartIndex_; - unsigned int numVoxels_; - - /// Internal vector, one for each pool species managed by Dsolve. - vector< DiffPoolVec > pools_; - /// Internal vector, one for each ConcChan managed by Dsolve. - vector< ConcChanInfo > channels_; - - /// smallest Id value for poolMap_ - unsigned int poolMapStart_; - - /// Looks up pool# from pool Id, using poolMapStart_ as offset. - vector< unsigned int > poolMap_; - - /** - * Lists all the diffusion junctions managed by this Dsolve. - * Each junction entry provides the info needed to do the - * numerical integration for flux between the Dsolves. - */ - vector< DiffJunction > junctions_; +public: + Dsolve(); + ~Dsolve(); + + ////////////////////////////////////////////////////////////////// + // Field assignment stuff + ////////////////////////////////////////////////////////////////// + unsigned int getNumVarPools() const; + + void setStoich( Id id ); + Id getStoich() const; + void setCompartment( Id id ); + // Defined in base class. Id getCompartment() const; + void setDsolve( Id id ); /// Dummy, inherited but not used. + + void setPath( const Eref& e, string path ); + string getPath( const Eref& e ) const; + + unsigned int getNumVoxels() const; + /// Inherited virtual. + void setNumAllVoxels( unsigned int numVoxels ); + + vector< double > getNvec( unsigned int pool ) const; + void setNvec( unsigned int pool, vector< double > vec ); + + /// LookupFied for examining cross-solver diffusion terms. + double getDiffVol1( unsigned int voxel ) const; + void setDiffVol1( unsigned int voxel, double vol ); + double getDiffVol2( unsigned int voxel ) const; + void setDiffVol2( unsigned int voxel, double vol ); + double getDiffScale( unsigned int voxel ) const; + void setDiffScale( unsigned int voxel, double scale ); + + ////////////////////////////////////////////////////////////////// + // Dest Finfos + ////////////////////////////////////////////////////////////////// + void process( const Eref& e, ProcPtr p ); + void reinit( const Eref& e, ProcPtr p ); + + ////////////////////////////////////////////////////////////////// + void updateJunctions( double dt ); + + /** + * Builds junctions between Dsolves handling NeuroMesh, SpineMesh, + * and PsdMesh. Must only be called from the one handling the + * NeuroMesh. + * These junctions handle diffusion between different meshes. + * Note that a single NeuroMesh may contact many spines which are + * all in a single SpineMesh. Likewise each spine has a single + * PSD, but there are many spines in the SpineMesh and matching + * psds in the PsdMesh. Finally, note that + * there may be many molecules which diffuse across each diffusion + * junction. + */ + void buildNeuroMeshJunctions( const Eref& e, Id spineD, Id psdD ); + + /** + * Builds junctions between current Dsolve and another. For this + * to work the respective meshes must be compatible. + * These junctions handle diffusion between different meshes. + */ + void buildMeshJunctions( const Eref& e, Id other ); + + /** + * buildMeshJunctions is the inner utility function for building + * the junction between any specified pair of Dsolves. + * Note that it builds the junction on the 'self' Dsolve. + */ + static void innerBuildMeshJunctions( Id self, Id other, + bool isMembraneBound ); + + /// Sets up map of matching pools for diffusion. + static void mapDiffPoolsBetweenDsolves( DiffJunction& jn, + Id self, Id other); + + static void mapXfersBetweenDsolves( + vector< unsigned int >& srcPools, + vector< unsigned int >& destPools, + Id src, Id dest ); + static void mapChansBetweenDsolves( DiffJunction& jn, + Id self, Id other); + + /** + * Computes flux through a junction between diffusion solvers. + * Most used at junctions on spines and PSDs, but can also be used + * when a given diff solver is decomposed. At present the lookups + * on the other diffusion solver assume that the data is on the + * local node. Once this works well I can figure out how to do + * across nodes. + */ + void calcJunction( const DiffJunction& jn, double dt ); + + /* Multithreaded version */ + void calcJunction_chunk( const size_t begin, const size_t end, double dt ); + + ////////////////////////////////////////////////////////////////// + // Inherited virtual funcs from ZombiePoolInterface + ////////////////////////////////////////////////////////////////// + double getNinit( const Eref& e ) const; + void setNinit( const Eref& e, double value ); + double getN( const Eref& e ) const; + void setN( const Eref& e, double value ); + double getDiffConst( const Eref& e ) const; + void setDiffConst( const Eref& e, double value ); + void setMotorConst( const Eref& e, double value ); + + void setNumPools( unsigned int num ); + unsigned int getNumPools() const; + unsigned int getNumLocalVoxels() const; + VoxelPoolsBase* pools( unsigned int i ); + double volume( unsigned int i ) const; + + void getBlock( vector< double >& values ) const; + void setBlock( const vector< double >& values ); + void setPrev(); + + // This one isn't used in Dsolve, but is defined as a dummy. + void setupCrossSolverReacs( + const map< Id, vector< Id > >& xr, Id otherStoich ); + void setupCrossSolverReacVols( + const vector< vector< Id > >& subCompts, + const vector< vector< Id > >& prdCompts ); + void filterCrossRateTerms( const vector< pair< Id, Id > >& xrt ); + + // Do any updates following a volume or rate constant change. + void updateRateTerms( unsigned int index ); + ////////////////////////////////////////////////////////////////// + // Model traversal and building functions + ////////////////////////////////////////////////////////////////// + unsigned int convertIdToPoolIndex( Id id ) const; + unsigned int convertIdToPoolIndex( const Eref& e ) const; + unsigned int getPoolIndex( const Eref& e ) const; + + /** + * Fills in poolMap_ using elist of objects found when the + * 'setPath' function is executed. temp is returned with the + * list of PoolBase objects that exist on the path. + */ + void makePoolMapFromElist( const vector< ObjId >& elist, + vector< Id >& temp ); + + /** + * This key function does the work of setting up the Dsolve. + * Should be called after the compartment has been attached to + * the Dsolve, and the stoich is assigned. + * Called during the setStoich function. + */ + void build( double dt ); + void rebuildPools(); + void calcJnDiff( const DiffJunction& jn, Dsolve* other, double dt ); + void calcJnXfer( const DiffJunction& jn, + const vector< unsigned int >& srcXfer, + const vector< unsigned int >& destXfer, + Dsolve* srcDsolve, Dsolve* destDsolve ); + void calcJnChan( const DiffJunction& jn, Dsolve* other, double dt ); + void calcOtherJnChan( const DiffJunction& jn, Dsolve* other, + double dt ); + void fillConcChans( const vector< ObjId >& chans ); + + /** + * Utility func for debugging: Prints N_ matrix + */ + void print() const; + + ////////////////////////////////////////////////////////////////// + static const Cinfo* initCinfo(); +private: + + /// Path of pools managed by Dsolve, may include other classes too. + string path_; + + /// Timestep used by diffusion calculations. + double dt_; + + unsigned int numTotPools_; + unsigned int numLocalPools_; + unsigned int poolStartIndex_; + unsigned int numVoxels_; + + /// Internal vector, one for each pool species managed by Dsolve. + vector< DiffPoolVec > pools_; + /// Internal vector, one for each ConcChan managed by Dsolve. + vector< ConcChanInfo > channels_; + + /// smallest Id value for poolMap_ + unsigned int poolMapStart_; + + /// Looks up pool# from pool Id, using poolMapStart_ as offset. + vector< unsigned int > poolMap_; + + /** + * Lists all the diffusion junctions managed by this Dsolve. + * Each junction entry provides the info needed to do the + * numerical integration for flux between the Dsolves. + */ + vector< DiffJunction > junctions_; }; diff --git a/external/muparser/include/muParserBase.h b/external/muparser/include/muParserBase.h index 774330cc..e7cfa6ae 100644 --- a/external/muparser/include/muParserBase.h +++ b/external/muparser/include/muParserBase.h @@ -288,7 +288,7 @@ private: mutable stringbuf_type m_vStringBuf; ///< String buffer, used for storing string function arguments stringbuf_type m_vStringVarBuf; - std::unique_ptr<token_reader_type> m_pTokenReader; ///< Managed pointer to the token reader object. + std::shared_ptr<token_reader_type> m_pTokenReader; ///< Managed pointer to the token reader object. funmap_type m_FunDef; ///< Map of function names and pointers. funmap_type m_PostOprtDef; ///< Postfix operator callbacks diff --git a/hsolve/HSolve.cpp b/hsolve/HSolve.cpp index e4b9b0e7..7f3cc36b 100644 --- a/hsolve/HSolve.cpp +++ b/hsolve/HSolve.cpp @@ -7,7 +7,8 @@ ** See the file COPYING.LIB for the full notice. **********************************************************************/ -#include "header.h" +#include "../basecode/header.h" +#include "../basecode/global.h" #include "ElementValueFinfo.h" #include "HSolveStruct.h" #include "HinesMatrix.h" @@ -27,6 +28,12 @@ #include "ZombieHHChannel.h" #include "../shell/Shell.h" +#include <chrono> +using namespace std::chrono; + +// defined in global.h +extern map<string, double> solverProfMap; + const Cinfo* HSolve::initCinfo() { static DestFinfo process( @@ -190,12 +197,20 @@ static const Cinfo* hsolveCinfo = HSolve::initCinfo(); HSolve::HSolve() : dt_( 50e-6 ) { - ; } HSolve::~HSolve() { unzombify(); +#if 0 + char* p = getenv( "MOOSE_SHOW_SOLVER_PERF" ); + if( p != NULL ) + { + cout << "Info: HSolve took " << totalTime_ << " seconds and took " << numSteps_ + << " steps." << endl; + + } +#endif } @@ -205,7 +220,10 @@ HSolve::~HSolve() void HSolve::process( const Eref& hsolve, ProcPtr p ) { + t0_ = high_resolution_clock::now(); this->HSolveActive::step( p ); + t1_ = high_resolution_clock::now(); + addSolverProf( "HSolve", duration_cast<duration<double>>(t1_ - t0_).count(), 1 ); } void HSolve::reinit( const Eref& hsolve, ProcPtr p ) diff --git a/hsolve/HSolve.h b/hsolve/HSolve.h index 56c15d2d..48317cee 100644 --- a/hsolve/HSolve.h +++ b/hsolve/HSolve.h @@ -9,166 +9,173 @@ #ifndef _HSOLVE_H #define _HSOLVE_H + #include <set> +#include <chrono> +using namespace std::chrono; + /** * HSolve adapts the integrator HSolveActive into a MOOSE class. */ class HSolve: public HSolveActive { public: - HSolve(); - ~HSolve(); + HSolve(); + ~HSolve(); - void process( const Eref& hsolve, ProcPtr p ); - void reinit( const Eref& hsolve, ProcPtr p ); + void process( const Eref& hsolve, ProcPtr p ); + void reinit( const Eref& hsolve, ProcPtr p ); - void setSeed( Id seed ); - Id getSeed() const; /**< For searching for compartments: + void setSeed( Id seed ); + Id getSeed() const; /**< For searching for compartments: * seed is the starting compt. */ - void setPath( const Eref& e, string path ); - string getPath( const Eref& e ) const; - /**< Path to the compartments */ + void setPath( const Eref& e, string path ); + string getPath( const Eref& e ) const; + /**< Path to the compartments */ - void setDt( double dt ); - double getDt() const; + void setDt( double dt ); + double getDt() const; - void setCaAdvance( int caAdvance ); - int getCaAdvance() const; + void setCaAdvance( int caAdvance ); + int getCaAdvance() const; - void setVDiv( int vDiv ); - int getVDiv() const; + void setVDiv( int vDiv ); + int getVDiv() const; - void setVMin( double vMin ); - double getVMin() const; + void setVMin( double vMin ); + double getVMin() const; - void setVMax( double vMax ); - double getVMax() const; + void setVMax( double vMax ); + double getVMax() const; - void setCaDiv( int caDiv ); - int getCaDiv() const; + void setCaDiv( int caDiv ); + int getCaDiv() const; - void setCaMin( double caMin ); - double getCaMin() const; + void setCaMin( double caMin ); + double getCaMin() const; - void setCaMax( double caMax ); - double getCaMax() const; + void setCaMax( double caMax ); + double getCaMax() const; - // Interface functions defined in HSolveInterface.cpp - double getInitVm( Id id ) const; - void setInitVm( Id id, double value ); + // Interface functions defined in HSolveInterface.cpp + double getInitVm( Id id ) const; + void setInitVm( Id id, double value ); - double getVm( Id id ) const; - void setVm( Id id, double value ); + double getVm( Id id ) const; + void setVm( Id id, double value ); - double getCm( Id id ) const; - void setCm( Id id, double value ); + double getCm( Id id ) const; + void setCm( Id id, double value ); - double getEm( Id id ) const; - void setEm( Id id, double value ); + double getEm( Id id ) const; + void setEm( Id id, double value ); - double getRm( Id id ) const; - void setRm( Id id, double value ); + double getRm( Id id ) const; + void setRm( Id id, double value ); - double getRa( Id id ) const; - void setRa( Id id, double value ); + double getRa( Id id ) const; + void setRa( Id id, double value ); - // Im is read-only - double getIm( Id id ) const; + // Im is read-only + double getIm( Id id ) const; - // Ia is read-only - double getIa( Id id ) const; + // Ia is read-only + double getIa( Id id ) const; - double getInject( Id id ) const; - void setInject( Id id, double value ); + double getInject( Id id ) const; + void setInject( Id id, double value ); - void addInject( Id id, double value ); + void addInject( Id id, double value ); - /// Interface to compartments - //~ const vector< Id >& getCompartments() const; + /// Interface to compartments + //~ const vector< Id >& getCompartments() const; - void addGkEk( Id id, double v1, double v2 ); - void addConc( Id id, double conc ); - /// Interface to channels - //~ const vector< Id >& getHHChannels() const; - void setPowers( - Id id, - double Xpower, - double Ypower, - double Zpower ); + void addGkEk( Id id, double v1, double v2 ); + void addConc( Id id, double conc ); + /// Interface to channels + //~ const vector< Id >& getHHChannels() const; + void setPowers( + Id id, + double Xpower, + double Ypower, + double Zpower ); - int getInstant( Id id ) const; - void setInstant( Id id, int instant ); + int getInstant( Id id ) const; + void setInstant( Id id, int instant ); - double getHHChannelGbar( Id id ) const; - void setHHChannelGbar( Id id, double value ); + double getHHChannelGbar( Id id ) const; + void setHHChannelGbar( Id id, double value ); - double getEk( Id id ) const; - void setEk( Id id, double value ); + double getEk( Id id ) const; + void setEk( Id id, double value ); - double getGk( Id id ) const; - void setGk( Id id, double value ); + double getGk( Id id ) const; + void setGk( Id id, double value ); - // Ik is read-only - double getIk( Id id ) const; + // Ik is read-only + double getIk( Id id ) const; - double getX( Id id ) const; - void setX( Id id, double value ); + double getX( Id id ) const; + void setX( Id id, double value ); - double getY( Id id ) const; - void setY( Id id, double value ); + double getY( Id id ) const; + void setY( Id id, double value ); - double getZ( Id id ) const; - void setZ( Id id, double value ); + double getZ( Id id ) const; + void setZ( Id id, double value ); - /// Assign scale factor for HH channel conductance. - void setHHmodulation( Id id, double value ); + /// Assign scale factor for HH channel conductance. + void setHHmodulation( Id id, double value ); - /// Interface to CaConc - //~ const vector< Id >& getCaConcs() const; - double getCa( Id id ) const; - void setCa( Id id, double Ca ); - void iCa( Id id, double iCa ); // Add incoming calcium current. + /// Interface to CaConc + //~ const vector< Id >& getCaConcs() const; + double getCa( Id id ) const; + void setCa( Id id, double Ca ); + void iCa( Id id, double iCa ); // Add incoming calcium current. - double getCaBasal( Id id ) const; - void setCaBasal( Id id, double CaBasal ); + double getCaBasal( Id id ) const; + void setCaBasal( Id id, double CaBasal ); - void setTauB( Id id, double tau, double B ); + void setTauB( Id id, double tau, double B ); - double getCaCeiling( Id id ) const; - void setCaCeiling( Id id, double floor ); + double getCaCeiling( Id id ) const; + void setCaCeiling( Id id, double floor ); - double getCaFloor( Id id ) const; - void setCaFloor( Id id, double floor ); + double getCaFloor( Id id ) const; + void setCaFloor( Id id, double floor ); - /// Interface to external channels - //~ const vector< vector< Id > >& getExternalChannels() const; + /// Interface to external channels + //~ const vector< vector< Id > >& getExternalChannels() const; - static const Cinfo* initCinfo(); + static const Cinfo* initCinfo(); static const std::set<string>& handledClasses(); - /**< Returns the set of classes "handled" by HSolve */ + /**< Returns the set of classes "handled" by HSolve */ static void deleteIncomingMessages( Element * orig, const string finfo); - /**< Delete messages coming into this particular - * element if its class that is handled by HSolve */ + /**< Delete messages coming into this particular + * element if its class that is handled by HSolve */ private: - static vector< Id > children( Id obj ); - static Id deepSearchForCompartment( Id base ); - - void setup( Eref hsolve ); - void zombify( Eref hsolve ) const; - void unzombify() const; - - // Mapping global Id to local index. Defined in HSolveInterface.cpp. - void mapIds(); - void mapIds( vector< Id > id ); - unsigned int localIndex( Id id ) const; - map< Id, unsigned int > localIndex_; - - double dt_; - string path_; - Id seed_; + static vector< Id > children( Id obj ); + static Id deepSearchForCompartment( Id base ); + + void setup( Eref hsolve ); + void zombify( Eref hsolve ) const; + void unzombify() const; + + // Mapping global Id to local index. Defined in HSolveInterface.cpp. + void mapIds(); + void mapIds( vector< Id > id ); + unsigned int localIndex( Id id ) const; + map< Id, unsigned int > localIndex_; + + double dt_; + string path_; + Id seed_; + + double totalTime_ = 0.0; + high_resolution_clock::time_point t0_, t1_; }; #endif // _HSOLVE_H diff --git a/ksolve/BoostSys.h b/ksolve/BoostSys.h index ae97723a..89fcc817 100644 --- a/ksolve/BoostSys.h +++ b/ksolve/BoostSys.h @@ -21,34 +21,6 @@ typedef boost::numeric::odeint::modified_midpoint< vector_type_ > rk_midpoint_st typedef boost::numeric::odeint::runge_kutta_cash_karp54< vector_type_ > rk_karp_stepper_type_; typedef boost::numeric::odeint::runge_kutta_fehlberg78< vector_type_ > rk_felhberg_stepper_type_; - -class VoxelPools; - -/* - * ===================================================================================== - * Class: BoostSys - * Description: The ode system of ksolve. It uses boost library to solve it. - * It is intended to be gsl replacement. - * ===================================================================================== - */ -class BoostSys -{ -public: - BoostSys( ); - ~BoostSys( ); - - /* Operator is called by boost ode-solver */ - void operator()( const vector_type_ y , vector_type_& dydt, const double t ); - - /* Pointer to the arbitrary parameters of the system */ - VoxelPools* vp; - void* params; - - double epsAbs; - double epsRel; - std::string method; -}; - #endif // USE_BOOST_ODE #endif /* end of include guard: BOOSTSYSTEM_H */ diff --git a/ksolve/CMakeLists.txt b/ksolve/CMakeLists.txt index f35588de..f86c69c0 100644 --- a/ksolve/CMakeLists.txt +++ b/ksolve/CMakeLists.txt @@ -18,11 +18,11 @@ elseif(WITH_GSL) include_directories( ${GSL_INCLUDE_DIRS} ) endif(WITH_BOOST_ODE) -if(PARALLELIZED_SOLVERS) - message( STATUS "Parallel version of KSolve and Gsolve" ) - add_definitions( -DPARALLELIZE_KSOLVE_WITH_CPP11_ASYNC ) - add_definitions( -DPARALLELIZE_GSOLVE_WITH_CPP11_ASYNC ) -endif(PARALLELIZED_SOLVERS) +if(WITH_BOOST) + find_package(Boost REQUIRED COMPONENTS thread) + add_definitions( -DUSE_BOOST_ASYNC ) + include_directories( ${Boost_INCLUDE_DIRS} ) +endif() set(KSOLVE_SRCS KinSparseMatrix.cpp @@ -48,7 +48,7 @@ if(WITH_GSL) list(APPEND KSOLVE_SRCS SteadyStateGsl.cpp ) elseif(WITH_BOOST_ODE) list(APPEND KSOLVE_SRCS SteadyStateBoost.cpp ) - list(APPEND KSOLVE_SRCS BoostSys.cpp) endif(WITH_GSL) add_library( ksolve ${KSOLVE_SRCS} ) +target_link_libraries( ksolve ${Boost_LIBRARIES} ) diff --git a/ksolve/FuncRateTerm.h b/ksolve/FuncRateTerm.h index 9dad5809..b04d7d0a 100644 --- a/ksolve/FuncRateTerm.h +++ b/ksolve/FuncRateTerm.h @@ -4,8 +4,7 @@ ** also known as GENESIS 3 base code. ** copyright (C) 2003-2010 Upinder S. Bhalla. and NCBS ** It is made available under the terms of the -** GNU Lesser General Public License version 2.1 -** See the file COPYING.LIB for the full notice. +** GNU Lesser General Public License version 3 or later. **********************************************************************/ /** @@ -21,63 +20,63 @@ */ class FuncRate: public ExternReac { - public: - FuncRate( double k, unsigned int targetPoolIndex ) - : k_( k ), funcVolPower_( 0.0 ) - { - func_.setTarget( targetPoolIndex ); - } - - double operator() ( const double* S ) const { - double t = Field< double >::get( Id(1), "currentTime" ); - return func_( S, t ); // get rate from func calculation. - } - - unsigned int getReactants( vector< unsigned int >& molIndex ) const{ - molIndex.resize( 1 ); - molIndex[0] = func_.getTarget(); - - // This is the number of substrates to the reac. It is zero. - return 0; - // The target molecule is handled as a product. - } - - void setReactants( const vector< unsigned int >& molIndex ) { - assert( molIndex.size() > 0 ); - func_.setTarget( molIndex[0] ); - } - - const vector< unsigned int >& getFuncArgIndex() - { - return func_.getReactantIndex(); - } - - void setFuncArgIndex( const vector< unsigned int >& mol ) { - func_.setReactantIndex( mol ); - } - - void setExpr( const string& s ) { - func_.setExpr( s ); - } - const string& getExpr() const { - return func_.getExpr(); - } - - RateTerm* copyWithVolScaling( - double vol, double sub, double prd ) const - { - double ratio = sub * pow( NA * vol, funcVolPower_ ); - FuncRate* ret = new FuncRate( k_ / ratio, func_.getTarget() ); - ret->funcVolPower_ = funcVolPower_; - ret->func_ = func_; - // return new FuncRate( k_ / ratio ); - return ret; - } - - protected: - FuncTerm func_; - double k_; - double funcVolPower_; + public: + FuncRate( double k, unsigned int targetPoolIndex ) + : k_( k ), funcVolPower_( 0.0 ) + { + func_.setTarget( targetPoolIndex ); + } + + double operator() ( const double* S ) const { + double t = Field< double >::get( Id(1), "currentTime" ); + return func_( S, t ); // get rate from func calculation. + } + + unsigned int getReactants( vector< unsigned int >& molIndex ) const{ + molIndex.resize( 1 ); + molIndex[0] = func_.getTarget(); + + // This is the number of substrates to the reac. It is zero. + return 0; + // The target molecule is handled as a product. + } + + void setReactants( const vector< unsigned int >& molIndex ) { + assert( molIndex.size() > 0 ); + func_.setTarget( molIndex[0] ); + } + + const vector< unsigned int >& getFuncArgIndex() + { + return func_.getReactantIndex(); + } + + void setFuncArgIndex( const vector< unsigned int >& mol ) { + func_.setReactantIndex( mol ); + } + + void setExpr( const string& s ) { + func_.setExpr( s ); + } + const string& getExpr() const { + return func_.getExpr(); + } + + RateTerm* copyWithVolScaling( + double vol, double sub, double prd ) const + { + double ratio = sub * pow( NA * vol, funcVolPower_ ); + FuncRate* ret = new FuncRate( k_ / ratio, func_.getTarget() ); + ret->funcVolPower_ = funcVolPower_; + ret->func_ = func_; + // return new FuncRate( k_ / ratio ); + return ret; + } + + protected: + FuncTerm func_; + double k_; + double funcVolPower_; }; @@ -98,57 +97,57 @@ class FuncRate: public ExternReac */ class FuncReac: public FuncRate { - public: - FuncReac( double k, vector< unsigned int > v ) - : FuncRate( k, 0 ), - v_( v ) - {;} - - double operator() ( const double* S ) const { - // double ret = k_ * func_( S, 0.0 ); // get rate from func calculation. - double ret = func_( S, 0.0 ); // get rate from func calculation. - vector< unsigned int >::const_iterator i; - for ( i = v_.begin(); i != v_.end(); i++) { - assert( !std::isnan( S[ *i ] ) ); - ret *= S[ *i ]; - } - return ret; - } - - unsigned int getReactants( vector< unsigned int >& molIndex ) const{ - molIndex = v_; - return numSubstrates_; - } - - void setReactants( const vector< unsigned int >& molIndex ) { - v_ = molIndex; - } - - void rescaleVolume( short comptIndex, - const vector< short >& compartmentLookup, double ratio ) - { - for ( unsigned int i = 1; i < v_.size(); ++i ) { - if ( comptIndex == compartmentLookup[ v_[i] ] ) - k_ /= ratio; - } - } - - - RateTerm* copyWithVolScaling( - double vol, double sub, double prd ) const - { - assert( v_.size() > 0 ); - double ratio = sub * pow( NA * vol, - funcVolPower_ + (int)( v_.size() ) - 1 ); - FuncReac* ret = new FuncReac( k_ / ratio, v_ ); - ret->func_ = func_; - ret->funcVolPower_ = funcVolPower_; - return ret; - // return new FuncReac( k_ / ratio, v_ ); - } - - private: - vector< unsigned int > v_; - unsigned int numSubstrates_; + public: + FuncReac( double k, vector< unsigned int > v ) + : FuncRate( k, 0 ), + v_( v ) + {;} + + double operator() ( const double* S ) const { + // double ret = k_ * func_( S, 0.0 ); // get rate from func calculation. + double ret = func_( S, 0.0 ); // get rate from func calculation. + vector< unsigned int >::const_iterator i; + for ( i = v_.begin(); i != v_.end(); i++) { + assert( !std::isnan( S[ *i ] ) ); + ret *= S[ *i ]; + } + return ret; + } + + unsigned int getReactants( vector< unsigned int >& molIndex ) const{ + molIndex = v_; + return numSubstrates_; + } + + void setReactants( const vector< unsigned int >& molIndex ) { + v_ = molIndex; + } + + void rescaleVolume( short comptIndex, + const vector< short >& compartmentLookup, double ratio ) + { + for ( unsigned int i = 1; i < v_.size(); ++i ) { + if ( comptIndex == compartmentLookup[ v_[i] ] ) + k_ /= ratio; + } + } + + + RateTerm* copyWithVolScaling( + double vol, double sub, double prd ) const + { + assert( v_.size() > 0 ); + double ratio = sub * pow( NA * vol, + funcVolPower_ + (int)( v_.size() ) - 1 ); + FuncReac* ret = new FuncReac( k_ / ratio, v_ ); + ret->func_ = func_; + ret->funcVolPower_ = funcVolPower_; + return ret; + // return new FuncReac( k_ / ratio, v_ ); + } + + private: + vector< unsigned int > v_; + unsigned int numSubstrates_; }; diff --git a/ksolve/FuncTerm.cpp b/ksolve/FuncTerm.cpp index b7a8ec1c..1ca12021 100644 --- a/ksolve/FuncTerm.cpp +++ b/ksolve/FuncTerm.cpp @@ -28,45 +28,45 @@ using namespace std; #include "../utility/numutil.h" FuncTerm::FuncTerm() - : reactantIndex_( 1, 0 ), - volScale_( 1.0 ), - target_( ~0U) + : reactantIndex_( 1, 0 ), + volScale_( 1.0 ), + target_( ~0U) { - args_ = 0; - parser_.DefineConst(_T("pi"), (mu::value_type)M_PI); - parser_.DefineConst(_T("e"), (mu::value_type)M_E); + args_ = 0; + parser_.DefineConst(_T("pi"), (mu::value_type)M_PI); + parser_.DefineConst(_T("e"), (mu::value_type)M_E); } FuncTerm::~FuncTerm() { - if (args_) { - delete[] args_; - } + if (args_) { + delete[] args_; + } } void FuncTerm::setReactantIndex( const vector< unsigned int >& mol ) { - reactantIndex_ = mol; - if ( args_ ) { - delete[] args_; - args_ = 0; - } - args_ = new double[ mol.size() + 1 ]; - // args_.resize( mol.size() + 1, 0.0 ); - for ( unsigned int i = 0; i < mol.size(); ++i ) { - stringstream ss; - args_[i] = 0.0; - ss << "x" << i; - parser_.DefineVar( ss.str(), &args_[i] ); - } - // Define a 't' variable even if we don't always use it. - args_[mol.size()] = 0.0; - parser_.DefineVar( "t", &args_[mol.size()] ); + reactantIndex_ = mol; + if ( args_ ) { + delete[] args_; + args_ = 0; + } + args_ = new double[ mol.size() + 1 ]; + // args_.resize( mol.size() + 1, 0.0 ); + for ( unsigned int i = 0; i < mol.size(); ++i ) { + stringstream ss; + args_[i] = 0.0; + ss << "x" << i; + parser_.DefineVar( ss.str(), &args_[i] ); + } + // Define a 't' variable even if we don't always use it. + args_[mol.size()] = 0.0; + parser_.DefineVar( "t", &args_[mol.size()] ); } const vector< unsigned int >& FuncTerm::getReactantIndex() const { - return reactantIndex_; + return reactantIndex_; } @@ -82,50 +82,50 @@ void showError(mu::Parser::exception_type &e) void FuncTerm::setExpr( const string& expr ) { - try { - parser_.SetExpr( expr ); - expr_ = expr; - } catch(mu::Parser::exception_type &e) { - showError(e); - //return; + try { + parser_.SetExpr( expr ); + expr_ = expr; + } catch(mu::Parser::exception_type &e) { + showError(e); + //return; throw(e); - } + } } const string& FuncTerm::getExpr() const { - return expr_; + return expr_; } void FuncTerm::setTarget( unsigned int t ) { - target_ = t; + target_ = t; } const unsigned int FuncTerm::getTarget() const { - return target_; + return target_; } void FuncTerm::setVolScale( double vs ) { - volScale_ = vs; + volScale_ = vs; } double FuncTerm::getVolScale() const { - return volScale_; + return volScale_; } const FuncTerm& FuncTerm::operator=( const FuncTerm& other ) { - args_ = 0; // Don't delete it, the original one is still using it. - parser_ = other.parser_; - expr_ = other.expr_; - volScale_ = other.volScale_; - target_ = other.target_; - setReactantIndex( other.reactantIndex_ ); - return *this; + args_ = 0; // Don't delete it, the original one is still using it. + parser_ = other.parser_; + expr_ = other.expr_; + volScale_ = other.volScale_; + target_ = other.target_; + setReactantIndex( other.reactantIndex_ ); + return *this; } /** @@ -134,12 +134,12 @@ const FuncTerm& FuncTerm::operator=( const FuncTerm& other ) */ double FuncTerm::operator() ( const double* S, double t ) const { - if ( !args_ ) - return 0.0; - unsigned int i; - for ( i = 0; i < reactantIndex_.size(); ++i ) - args_[i] = S[reactantIndex_[i]]; - args_[i] = t; + if ( !args_ ) + return 0.0; + unsigned int i; + for ( i = 0; i < reactantIndex_.size(); ++i ) + args_[i] = S[reactantIndex_[i]]; + args_[i] = t; try { double result = parser_.Eval() * volScale_; @@ -155,12 +155,12 @@ double FuncTerm::operator() ( const double* S, double t ) const void FuncTerm::evalPool( double* S, double t ) const { - if ( !args_ || target_ == ~0U ) - return; - unsigned int i; - for ( i = 0; i < reactantIndex_.size(); ++i ) - args_[i] = S[reactantIndex_[i]]; - args_[i] = t; + if ( !args_ || target_ == ~0U ) + return; + unsigned int i; + for ( i = 0; i < reactantIndex_.size(); ++i ) + args_[i] = S[reactantIndex_[i]]; + args_[i] = t; try { S[ target_] = parser_.Eval() * volScale_; diff --git a/ksolve/FuncTerm.h b/ksolve/FuncTerm.h index 05779b79..ccf2dcd1 100644 --- a/ksolve/FuncTerm.h +++ b/ksolve/FuncTerm.h @@ -14,44 +14,45 @@ class FuncTerm { - public: - FuncTerm(); - ~FuncTerm(); - /** - * This computes the value. The time t is an argument needed by - * some functions. - */ - double operator() ( const double* S, double t ) const; - const FuncTerm& operator=( const FuncTerm& other ); + public: + FuncTerm(); + ~FuncTerm(); + /** + * This computes the value. The time t is an argument needed by + * some functions. + */ + double operator() ( const double* S, double t ) const; + const FuncTerm& operator=( const FuncTerm& other ); - void evalPool( double* s, double t ) const; + void evalPool( double* s, double t ) const; - /** - * This function finds the reactant indices in the vector - * S. It returns the number of indices found, which are the - * entries in molIndex. - */ - void setReactantIndex( const vector< unsigned int >& mol ); - const vector< unsigned int >& getReactantIndex() const; - const string& getExpr() const; - void setExpr( const string& e ); - const unsigned int getTarget() const; - void setTarget( unsigned int tgt ); - void setVolScale( double vs ); - double getVolScale() const; - private: - double* args_; - // Look up reactants in the S vec. - vector< unsigned int > reactantIndex_; - mu::Parser parser_; - string expr_; - /** - * Scale factor to account for pool volume if we are assigning conc - * rather than N. Note that this conc will not be further updated - * so this is an undesirable option. - */ - double volScale_; - unsigned int target_; /// Index of the entity to be updated by Func + /** + * This function finds the reactant indices in the vector + * S. It returns the number of indices found, which are the + * entries in molIndex. + */ + void setReactantIndex( const vector< unsigned int >& mol ); + const vector< unsigned int >& getReactantIndex() const; + const string& getExpr() const; + void setExpr( const string& e ); + const unsigned int getTarget() const; + void setTarget( unsigned int tgt ); + void setVolScale( double vs ); + double getVolScale() const; + + private: + double* args_; + // Look up reactants in the S vec. + vector< unsigned int > reactantIndex_; + mu::Parser parser_; + string expr_; + /** + * Scale factor to account for pool volume if we are assigning conc + * rather than N. Note that this conc will not be further updated + * so this is an undesirable option. + */ + double volScale_; + unsigned int target_; /// Index of the entity to be updated by Func }; #endif // _FUNC_TERM_H diff --git a/ksolve/Gsolve.cpp b/ksolve/Gsolve.cpp index c7f5cb8a..d99dd12a 100644 --- a/ksolve/Gsolve.cpp +++ b/ksolve/Gsolve.cpp @@ -23,12 +23,22 @@ #include "GssaSystem.h" #include "Stoich.h" #include "GssaVoxelPools.h" +#include "Gsolve.h" + +#include <chrono> +#include <algorithm> + +#ifdef USE_BOOST_ASYNC +#define BOOST_THREAD_PROVIDES_FUTURE +#include <boost/thread.hpp> +#include <boost/thread/future.hpp> +#endif #include <future> #include <atomic> #include <thread> +#include <functional> -#include "Gsolve.h" #define SIMPLE_ROUNDING 0 @@ -60,13 +70,14 @@ const Cinfo* Gsolve::initCinfo() "current solver. ", &Gsolve::getNumLocalVoxels ); - static LookupValueFinfo< - Gsolve, unsigned int, vector< double > > nVec( + + static LookupValueFinfo<Gsolve, unsigned int, vector< double > > nVec( "nVec", "vector of pool counts", &Gsolve::setNvec, &Gsolve::getNvec ); + static ValueFinfo< Gsolve, unsigned int > numAllVoxels( "numAllVoxels", "Number of voxels in the entire reac-diff system, " @@ -83,6 +94,13 @@ const Cinfo* Gsolve::initCinfo() &Gsolve::getNumPools ); + static ValueFinfo< Gsolve, unsigned int > numThreads( + "numThreads", + "Number of threads to use in GSolve", + &Gsolve::setNumThreads, + &Gsolve::getNumThreads + ); + static ValueFinfo< Gsolve, bool > useRandInit( "useRandInit", "Flag: True when using probabilistic (random) rounding.\n " @@ -107,7 +125,7 @@ const Cinfo* Gsolve::initCinfo() "This flag should be set when the reaction system " "includes a function with a dependency on time or on external " "events. It has a significant speed penalty so the flag " - "should not be set unless there are such functions. " , + "should not be set unless there are such functions. ", &Gsolve::setClockedUpdate, &Gsolve::getClockedUpdate ); @@ -171,28 +189,29 @@ const Cinfo* Gsolve::initCinfo() static Finfo* gsolveFinfos[] = { - &stoich, // Value - &numLocalVoxels, // ReadOnlyValue - &nVec, // LookupValue - &numAllVoxels, // ReadOnlyValue - &numPools, // Value - &voxelVol, // DestFinfo - &proc, // SharedFinfo - &init, // SharedFinfo + &stoich, // Value + &numLocalVoxels, // ReadOnlyValue + &nVec, // LookupValue + &numAllVoxels, // ReadOnlyValue + &numPools, // Value + &numThreads, // Value + &voxelVol, // DestFinfo + &proc, // SharedFinfo + &init, // SharedFinfo // Here we put new fields that were not there in the Ksolve. - &useRandInit, // Value - &useClockedUpdate, // Value - &numFire, // ReadOnlyLookupValue + &useRandInit, // Value + &useClockedUpdate, // Value + &numFire, // ReadOnlyLookupValue }; static Dinfo< Gsolve > dinfo; - static Cinfo gsolveCinfo( - "Gsolve", - Neutral::initCinfo(), - gsolveFinfos, - sizeof(gsolveFinfos)/sizeof(Finfo *), - &dinfo - ); + + static Cinfo gsolveCinfo( "Gsolve", + Neutral::initCinfo(), + gsolveFinfos, + sizeof(gsolveFinfos)/sizeof(Finfo *), + &dinfo + ); return &gsolveCinfo; } @@ -204,9 +223,7 @@ static const Cinfo* gsolveCinfo = Gsolve::initCinfo(); ////////////////////////////////////////////////////////////// Gsolve::Gsolve() : -#if PARALLELIZE_GSOLVE_WITH_CPP11_ASYNC - numThreads_ ( 2 ), -#endif + numThreads_ ( 1 ), pools_( 1 ), startVoxel_( 0 ), dsolve_(), @@ -360,44 +377,12 @@ void Gsolve::setClockedUpdate( bool val ) } -#if PARALLELIZE_GSOLVE_WITH_CPP11_ASYNC -/** - * @brief Advance voxels pools but concurrently. - * - * @param begin - * @param end - * @param p - */ -void Gsolve::parallel_advance(int begin, int end, size_t nWorkers, const ProcPtr p - , const GssaSystem* sys - ) -{ - std::atomic<int> idx( begin ); - for (size_t cpu = 0; cpu != nWorkers; ++cpu) - { - std::async( std::launch::async, - [this, &idx, end, p, sys]() - { - for (;;) - { - int i = idx++; - if (i >= end) - break; - pools_[i].advance( p, sys); - } - } - ); - } -} - -#endif - ////////////////////////////////////////////////////////////// // Process operations. ////////////////////////////////////////////////////////////// void Gsolve::process( const Eref& e, ProcPtr p ) { - // cout << stoichPtr_ << " dsolve = " << dsolvePtr_ << endl; + // cout << stoichPtr_ << " dsolve = " << dsolvePtr_ << endl; if ( !stoichPtr_ ) return; @@ -420,7 +405,7 @@ void Gsolve::process( const Eref& e, ProcPtr p ) for ( ; i != dvalues.end(); ++i ) { - // cout << *i << " " << round( *i ) << " "; + // cout << *i << " " << round( *i ) << " "; #if SIMPLE_ROUNDING *i = round( *i ); #else @@ -440,21 +425,29 @@ void Gsolve::process( const Eref& e, ProcPtr p ) // happening. This is very inefficient at this point, need to fix. if ( dsolvePtr_ ) { - for ( vector< GssaVoxelPools >::iterator - i = pools_.begin(); i != pools_.end(); ++i ) - { + for ( auto i = pools_.begin(); i != pools_.end(); ++i ) i->refreshAtot( &sys_ ); - } } + // Fourth, update the mol #s. // First we advance the simulation. size_t nvPools = pools_.size( ); -#if PARALLELIZE_GSOLVE_WITH_CPP11_ASYNC - // If there is only one voxel-pool or one thread is specified by user then - // there is no point in using std::async there. - if( 1 == getNumThreads( ) || 1 == nvPools ) + // Third, do the numerical integration for all reactions. + size_t grainSize = max( (size_t)1, min( nvPools, nvPools / numThreads_)); + + // Make sure that we cover all the pools. + while( (numThreads_ * grainSize) < nvPools ) + grainSize += 1; + + if( 1 == numThreads_ || 1 == nvPools ) { + if( numThreads_ > 1 ) + { + cerr << "Warn: Not enough voxels or threads. Reverting to serial mode. " << endl; + numThreads_ = 1; + } + for ( size_t i = 0; i < nvPools; i++ ) pools_[i].advance( p, &sys_ ); } @@ -464,22 +457,47 @@ void Gsolve::process( const Eref& e, ProcPtr p ) * Somewhat complicated computation to compute the number of threads. 1 * thread per (at least) voxel pool is ideal situation. *-----------------------------------------------------------------------------*/ - size_t grainSize = min( nvPools, 1 + (nvPools / numThreads_ ) ); - size_t nWorkers = nvPools / grainSize; + vector<std::thread> vecThreads; + + for (size_t i = 0; i < numThreads_; i++) + { + // Use lambda. It is roughly 10% faster than std::bind and does not + // involve copying data. + std::thread t( + [this, i, grainSize, p](){ this->advance_chunk(i*grainSize, (i+1)*grainSize, p); } + ); + vecThreads.push_back( std::move(t) ); + } - for (size_t i = 0; i < nWorkers; i++) - parallel_advance( i * grainSize, (i+1) * grainSize, nWorkers, p, &sys_ ); + for( auto &v : vecThreads ) + v.join(); } -#else - for ( size_t i = 0; i < nvPools; i++ ) - pools_[i].advance( p, &sys_ ); -#endif if ( useClockedUpdate_ ) // Check if a clocked stim is to be updated { - for ( auto &v : pools_ ) - v.recalcTime( &sys_, p->currTime ); + if(numThreads_ == 1) + { + for ( auto &v : pools_ ) + v.recalcTime( &sys_, p->currTime ); + } + else + { + vector<std::thread> vecThreads; + + for (size_t i = 0; i < numThreads_; i++) + { + // Use lambda. It is roughly 10% faster than std::bind and does not + // involve copying data. + std::thread t( + [this, i, grainSize, p](){ this->recalcTimeChunk(i*grainSize, (i+1)*grainSize, p); } + ); + vecThreads.push_back( std::move(t) ); + } + + for( auto &v : vecThreads ) + v.join(); + } } // Finally, assemble and send the integrated values off for the Dsolve. @@ -493,13 +511,25 @@ void Gsolve::process( const Eref& e, ProcPtr p ) getBlock( kvalues ); dsolvePtr_->setBlock( kvalues ); - // Now use the values in the Dsolve to update junction fluxes - // for diffusion, channels, and xreacs - dsolvePtr_->updateJunctions( p->dt ); - // Here the Gsolve may need to do something to convert to integers + // Now use the values in the Dsolve to update junction fluxes + // for diffusion, channels, and xreacs + dsolvePtr_->updateJunctions( p->dt ); + // Here the Gsolve may need to do something to convert to integers } } +void Gsolve::recalcTimeChunk( const size_t begin, const size_t end, ProcPtr p) +{ + for (size_t i = begin; i < std::min(pools_.size(), end); i++) + pools_[i].recalcTime( &sys_, p->currTime ); +} + +void Gsolve::advance_chunk( const size_t begin, const size_t end, ProcPtr p ) +{ + for (size_t i = begin; i < std::min(end, pools_.size() ); i++) + pools_[i].advance( p, &sys_ ); +} + void Gsolve::reinit( const Eref& e, ProcPtr p ) { if ( !stoichPtr_ ) @@ -519,11 +549,9 @@ void Gsolve::reinit( const Eref& e, ProcPtr p ) i->refreshAtot( &sys_ ); } -#if PARALLELIZE_GSOLVE_WITH_CPP11_ASYNC if( 1 < getNumThreads( ) ) - cout << "Info: Using threaded gsolve: " << getNumThreads( ) - << " threads. " << endl; -#endif + cout << "Info: Setting up threaded gsolve with " << getNumThreads( ) + << " threads. " << endl; } ////////////////////////////////////////////////////////////// @@ -752,48 +780,48 @@ void Gsolve::fillIncrementFuncDep() /* void Gsolve::fillMathDep() { - // create map of funcs that depend on specified molecule. - vector< vector< unsigned int > > funcMap( - stoichPtr_->getNumAllPools() ); - unsigned int numFuncs = stoichPtr_->getNumFuncs(); - for ( unsigned int i = 0; i < numFuncs; ++i ) { - const FuncTerm *f = stoichPtr_->funcs( i ); - vector< unsigned int > molIndex = f->getReactantIndex(); - for ( unsigned int j = 0; j < molIndex.size(); ++j ) - funcMap[ molIndex[j] ].push_back( i ); - } - // The output of each func is a mol indexed as - // numVarMols + numBufMols + i - unsigned int funcOffset = - stoichPtr_->getNumVarPools() + stoichPtr_->getNumProxyPools() + stoichPtr_->getNumBufPools(); - unsigned int numRates = stoichPtr_->getNumRates(); - sys_.dependentMathExpn.resize( numRates ); - vector< unsigned int > indices; - for ( unsigned int i = 0; i < numRates; ++i ) { - vector< unsigned int >& dep = sys_.dependentMathExpn[ i ]; - dep.resize( 0 ); - // Extract the row of all molecules that depend on the reac. - const int* entry; - const unsigned int* colIndex; - unsigned int numInRow = - sys_.transposeN.getRow( i, &entry, &colIndex ); - for ( unsigned int j = 0; j < numInRow; ++j ) { - unsigned int molIndex = colIndex[j]; - vector< unsigned int >& funcs = funcMap[ molIndex ]; - dep.insert( dep.end(), funcs.begin(), funcs.end() ); - for ( unsigned int k = 0; k < funcs.size(); ++k ) { - unsigned int outputMol = funcs[k] + funcOffset; - // Insert reac deps here. Columns are reactions. - vector< int > e; // Entries: we don't need. - vector< unsigned int > c; // Column index: the reactions. - stoichPtr_->getStoichiometryMatrix(). - getRow( outputMol, e, c ); - // Each of the reacs (col entries) depend on this func. - vector< unsigned int > rdep = sys_.dependency[i]; - rdep.insert( rdep.end(), c.begin(), c.end() ); - } - } - } + // create map of funcs that depend on specified molecule. + vector< vector< unsigned int > > funcMap( + stoichPtr_->getNumAllPools() ); + unsigned int numFuncs = stoichPtr_->getNumFuncs(); + for ( unsigned int i = 0; i < numFuncs; ++i ) { + const FuncTerm *f = stoichPtr_->funcs( i ); + vector< unsigned int > molIndex = f->getReactantIndex(); + for ( unsigned int j = 0; j < molIndex.size(); ++j ) + funcMap[ molIndex[j] ].push_back( i ); + } + // The output of each func is a mol indexed as + // numVarMols + numBufMols + i + unsigned int funcOffset = + stoichPtr_->getNumVarPools() + stoichPtr_->getNumProxyPools() + stoichPtr_->getNumBufPools(); + unsigned int numRates = stoichPtr_->getNumRates(); + sys_.dependentMathExpn.resize( numRates ); + vector< unsigned int > indices; + for ( unsigned int i = 0; i < numRates; ++i ) { + vector< unsigned int >& dep = sys_.dependentMathExpn[ i ]; + dep.resize( 0 ); + // Extract the row of all molecules that depend on the reac. + const int* entry; + const unsigned int* colIndex; + unsigned int numInRow = + sys_.transposeN.getRow( i, &entry, &colIndex ); + for ( unsigned int j = 0; j < numInRow; ++j ) { + unsigned int molIndex = colIndex[j]; + vector< unsigned int >& funcs = funcMap[ molIndex ]; + dep.insert( dep.end(), funcs.begin(), funcs.end() ); + for ( unsigned int k = 0; k < funcs.size(); ++k ) { + unsigned int outputMol = funcs[k] + funcOffset; + // Insert reac deps here. Columns are reactions. + vector< int > e; // Entries: we don't need. + vector< unsigned int > c; // Column index: the reactions. + stoichPtr_->getStoichiometryMatrix(). + getRow( outputMol, e, c ); + // Each of the reacs (col entries) depend on this func. + vector< unsigned int > rdep = sys_.dependency[i]; + rdep.insert( rdep.end(), c.begin(), c.end() ); + } + } + } } */ @@ -811,8 +839,8 @@ void Gsolve::insertMathDepReacs( unsigned int mathDepIndex, // Extract the row of all reacs that depend on the target molecule if ( N_.getRowIndices( molIndex, reacIndices ) > 0 ) { - vector< unsigned int >& dep = dependency_[ firedReac ]; - dep.insert( dep.end(), reacIndices.begin(), reacIndices.end() ); + vector< unsigned int >& dep = dependency_[ firedReac ]; + dep.insert( dep.end(), reacIndices.begin(), reacIndices.end() ); } */ } @@ -1063,7 +1091,6 @@ double Gsolve::volume( unsigned int i ) const return 0.0; } -#if PARALLELIZE_GSOLVE_WITH_CPP11_ASYNC unsigned int Gsolve::getNumThreads( ) const { return numThreads_; @@ -1073,4 +1100,3 @@ void Gsolve::setNumThreads( unsigned int x ) { numThreads_ = x; } -#endif diff --git a/ksolve/Gsolve.h b/ksolve/Gsolve.h index cb511814..4b09049c 100644 --- a/ksolve/Gsolve.h +++ b/ksolve/Gsolve.h @@ -108,12 +108,9 @@ public: */ void updateRateTerms( unsigned int index ); - - // A wrapper to call advance function of GssaVoxelPool - // concurrently. - void parallel_advance(int begin, int end, size_t nWorkers - , ProcPtr p , const GssaSystem* sys - ); + // Function for multithreading. + void advance_chunk( const size_t begin, const size_t end, ProcPtr p ); + void recalcTimeChunk( const size_t begin, const size_t end, ProcPtr p); ////////////////////////////////////////////////////////////////// /// Flag: returns true if randomized round to integers is done. @@ -126,22 +123,18 @@ public: /// Flag: set true if randomized round to integers is to be done. void setClockedUpdate( bool val ); -#if PARALLELIZE_GSOLVE_WITH_CPP11_ASYNC unsigned int getNumThreads( ) const; void setNumThreads( unsigned int x ); -#endif ////////////////////////////////////////////////////////////////// static const Cinfo* initCinfo(); private: -#if PARALLELIZE_GSOLVE_WITH_CPP11_ASYNC /** * @brief Number of threads to use when parallel version of Gsolve is * used. */ unsigned int numThreads_; -#endif GssaSystem sys_; /** @@ -169,6 +162,8 @@ private: /// Flag: True if atot should be updated every clock tick bool useClockedUpdate_; + + double t1_; }; #endif // _GSOLVE_H diff --git a/ksolve/GssaVoxelPools.cpp b/ksolve/GssaVoxelPools.cpp index 4128d0a6..ae725120 100644 --- a/ksolve/GssaVoxelPools.cpp +++ b/ksolve/GssaVoxelPools.cpp @@ -208,11 +208,7 @@ void GssaVoxelPools::advance( const ProcInfo* p, const GssaSystem* g ) assert( rindex < v_.size() ); } -#if ENABLE_CPP11 double sign = std::copysign( 1, v_[rindex] ); -#else - double sign = double(v_[rindex] >= 0) - double(0 > v_[rindex] ); -#endif g->transposeN.fireReac( rindex, Svec(), sign ); numFire_[rindex]++; @@ -238,6 +234,7 @@ void GssaVoxelPools::reinit( const GssaSystem* g ) double* n = varS(); + double totalN = 0; if( g->useRandInit ) { vector<double> error(numVarPools, 0.0); @@ -248,6 +245,7 @@ void GssaVoxelPools::reinit( const GssaSystem* g ) error[i] = n[i]; double base = std::floor( n[i] ); assert( base >= 0.0 ); + totalN += n[i]; double frac = n[i] - base; if ( rng_.uniform() >= frac ) n[i] = base; @@ -255,38 +253,28 @@ void GssaVoxelPools::reinit( const GssaSystem* g ) n[i] = base + 1.0; error[i] -= n[i]; - - //if( true ) - //{ - // //NOTE: Thats how I get the name of the pool at this index. - // Eref e = g->stoich->getPoolByIndex( i ).eref(); - // groupByVal[n[i]].push_back( e ); - - // // Guess the fix the error. - //} - - } double extra = std::accumulate( error.begin(), error.end(), 0.0 ); - if( std::abs(extra) > 0.1 ) + + // Show warning to user if extra molecules in the system after + // converting flots to integer is larger 1%. + if( std::abs(extra) / totalN > 0.01 ) { - cout << "WARNING: Extra " << extra - << " molecules in system after converting fractional to integer e.g. 1.1 = 1 (~90% of times) or 2 (~10% of times)." << endl; + cout << "Warn: Extra " << extra + << " molecules in system after converting fractional to integer e.g. 1.1 becomes " + " 1 roughly 90% of times or 2 roughly 10% of times." + << endl; } } else // Just round to the nearest int. { for ( unsigned int i = 0; i < numVarPools; ++i ) { -#if ENABLE_CPP11 // Just like rint but does not raise exception. // See http://en.cppreference.com/w/cpp/numeric/math/nearbyint for // details. n[i] = std::nearbyint(n[i]); -#else - n[i] = round( n[i] ); -#endif } } diff --git a/ksolve/Ksolve.cpp b/ksolve/Ksolve.cpp index b55d0d12..2277e5ff 100644 --- a/ksolve/Ksolve.cpp +++ b/ksolve/Ksolve.cpp @@ -7,6 +7,7 @@ ** See the file COPYING.LIB for the full notice. **********************************************************************/ #include "../basecode/header.h" +#include "../basecode/global.h" #ifdef USE_GSL #include <gsl/gsl_errno.h> #include <gsl/gsl_matrix.h> @@ -31,10 +32,22 @@ #include "../mesh/ChemCompt.h" #include "Ksolve.h" +#include <chrono> +#include <algorithm> + +#ifdef USE_BOOST_ASYNC +#define BOOST_THREAD_PROVIDES_FUTURE +#include <boost/thread.hpp> +#include <boost/thread/future.hpp> +#endif + #include <future> +#include <functional> #include <atomic> #include <thread> +using namespace std::chrono; + const unsigned int OFFNODE = ~0; const Cinfo* Ksolve::initCinfo() @@ -42,7 +55,6 @@ const Cinfo* Ksolve::initCinfo() /////////////////////////////////////////////////////// // Field definitions /////////////////////////////////////////////////////// - static ValueFinfo< Ksolve, string > method ( "method", "Integration method, using GSL. So far only explict. Options are:" @@ -84,13 +96,14 @@ const Cinfo* Ksolve::initCinfo() "current solver. ", &Ksolve::getNumLocalVoxels ); - static LookupValueFinfo< - Ksolve, unsigned int, vector< double > > nVec( + + static LookupValueFinfo< Ksolve, unsigned int, vector< double > > nVec( "nVec", "vector of pool counts. Index specifies which voxel.", &Ksolve::setNvec, &Ksolve::getNvec ); + static ValueFinfo< Ksolve, unsigned int > numAllVoxels( "numAllVoxels", "Number of voxels in the entire reac-diff system, " @@ -99,14 +112,12 @@ const Cinfo* Ksolve::initCinfo() &Ksolve::getNumAllVoxels ); -#if PARALLELIZE_KSOLVE_WITH_CPP11_ASYNC static ValueFinfo< Ksolve, unsigned int > numThreads ( "numThreads", "Number of threads to use (applicable in deterministic case)", &Ksolve::setNumThreads, &Ksolve::getNumThreads ); -#endif static ValueFinfo< Ksolve, unsigned int > numPools( "numPools", @@ -121,6 +132,7 @@ const Cinfo* Ksolve::initCinfo() "Estimated timestep for reac system based on Euler error", &Ksolve::getEstimatedDt ); + static ReadOnlyValueFinfo< Ksolve, Id > stoich( "stoich", "Id for stoichiometry object tied to this Ksolve", @@ -134,17 +146,23 @@ const Cinfo* Ksolve::initCinfo() static DestFinfo process( "process", "Handles process call from Clock", - new ProcOpFunc< Ksolve >( &Ksolve::process ) ); + new ProcOpFunc< Ksolve >( &Ksolve::process ) + ); + static DestFinfo reinit( "reinit", "Handles reinit call from Clock", - new ProcOpFunc< Ksolve >( &Ksolve::reinit ) ); + new ProcOpFunc< Ksolve >( &Ksolve::reinit ) + ); static DestFinfo initProc( "initProc", "Handles initProc call from Clock", - new ProcOpFunc< Ksolve >( &Ksolve::initProc ) ); + new ProcOpFunc< Ksolve >( &Ksolve::initProc ) + ); + static DestFinfo initReinit( "initReinit", "Handles initReinit call from Clock", - new ProcOpFunc< Ksolve >( &Ksolve::initReinit ) ); + new ProcOpFunc< Ksolve >( &Ksolve::initReinit ) + ); static DestFinfo voxelVol( "voxelVol", "Handles updates to all voxels. Comes from parent " @@ -152,6 +170,7 @@ const Cinfo* Ksolve::initCinfo() new OpFunc1< Ksolve, vector< double > >( &Ksolve::updateVoxelVol ) ); + /////////////////////////////////////////////////////// // Shared definitions /////////////////////////////////////////////////////// @@ -159,16 +178,19 @@ const Cinfo* Ksolve::initCinfo() { &process, &reinit }; + static SharedFinfo proc( "proc", "Shared message for process and reinit. These are used for " "all regular Ksolve calculations including interfacing with " "the diffusion calculations by a Dsolve.", procShared, sizeof( procShared ) / sizeof( const Finfo* ) ); + static Finfo* initShared[] = { &initProc, &initReinit }; + static SharedFinfo init( "init", "Shared message for initProc and initReinit. This is used" " when the system has cross-compartment reactions. ", @@ -177,22 +199,20 @@ const Cinfo* Ksolve::initCinfo() static Finfo* ksolveFinfos[] = { - &method, // Value - &epsAbs, // Value - &epsRel , // Value -#if PARALLELIZE_KSOLVE_WITH_CPP11_ASYNC - &numThreads, // Value -#endif - &compartment, // Value - &numLocalVoxels, // ReadOnlyValue - &nVec, // LookupValue - &numAllVoxels, // ReadOnlyValue - &numPools, // Value - &estimatedDt, // ReadOnlyValue - &stoich, // ReadOnlyValue - &voxelVol, // DestFinfo - &proc, // SharedFinfo - &init, // SharedFinfo + &method, // Value + &epsAbs, // Value + &epsRel , // Value + &numThreads, // Value + &compartment, // Value + &numLocalVoxels, // ReadOnlyValue + &nVec, // LookupValue + &numAllVoxels, // ReadOnlyValue + &numPools, // Value + &estimatedDt, // ReadOnlyValue + &stoich, // ReadOnlyValue + &voxelVol, // DestFinfo + &proc, // SharedFinfo + &init, // SharedFinfo }; static Dinfo< Ksolve > dinfo; @@ -222,20 +242,25 @@ Ksolve::Ksolve() #endif epsAbs_( 1e-7 ), epsRel_( 1e-7 ), -#if PARALLELIZE_KSOLVE_WITH_CPP11_ASYNC - numThreads_( 3 ), -#endif + numThreads_( 1 ), pools_( 1 ), startVoxel_( 0 ), dsolve_(), dsolvePtr_( 0 ) { - ; } Ksolve::~Ksolve() { - ; +#if 0 + char* p = getenv( "MOOSE_SHOW_SOLVER_PERF" ); + if( p != NULL ) + { + cout << "Info: Ksolve (+Dsolve) took " << totalTime_ << " seconds and took " << numSteps_ + << " steps." << endl; + + } +#endif } ////////////////////////////////////////////////////////////// @@ -302,7 +327,6 @@ void Ksolve::setEpsRel( double epsRel ) } } -#if PARALLELIZE_KSOLVE_WITH_CPP11_ASYNC void Ksolve::setNumThreads( unsigned int x ) { numThreads_ = x; @@ -312,7 +336,6 @@ unsigned int Ksolve::getNumThreads( ) const { return numThreads_; } -#endif Id Ksolve::getStoich() const { @@ -365,10 +388,11 @@ void Ksolve::setStoich( Id stoich ) ode.method = method_; #ifdef USE_GSL ode.gslSys.dimension = stoichPtr_->getNumAllPools(); - if ( ode.gslSys.dimension == 0 ) { - stoichPtr_ = 0; + if ( ode.gslSys.dimension == 0 ) + { + stoichPtr_ = 0; return; // No pools, so don't bother. - } + } innerSetMethod( ode, method_ ); ode.gslSys.function = &VoxelPools::gslFunc; ode.gslSys.jacobian = 0; @@ -382,15 +406,11 @@ void Ksolve::setStoich( Id stoich ) } #elif USE_BOOST_ODE ode.dimension = stoichPtr_->getNumAllPools(); - ode.boostSys.epsAbs = epsAbs_; - ode.boostSys.epsRel = epsRel_; - ode.boostSys.method = method_; if ( ode.dimension == 0 ) return; // No pools, so don't bother. unsigned int numVoxels = pools_.size(); for ( unsigned int i = 0 ; i < numVoxels; ++i ) { - ode.boostSys.params = &pools_[i]; pools_[i].setStoich( stoichPtr_, &ode ); } #endif @@ -496,9 +516,13 @@ double Ksolve::getEstimatedDt() const ////////////////////////////////////////////////////////////// void Ksolve::process( const Eref& e, ProcPtr p ) { + + if ( isBuilt_ == false ) return; + t0_ = high_resolution_clock::now(); + // First, handle incoming diffusion values, update S with those. if ( dsolvePtr_ ) { @@ -509,29 +533,30 @@ void Ksolve::process( const Eref& e, ProcPtr p ) dvalues[3] = stoichPtr_->getNumVarPools(); dsolvePtr_->getBlock( dvalues ); - // Second, set the prev_ value in DiffPoolVec - dsolvePtr_->setPrev(); + // Second, set the prev_ value in DiffPoolVec + dsolvePtr_->setPrev(); setBlock( dvalues ); } size_t nvPools = pools_.size( ); -#ifdef PARALLELIZE_KSOLVE_WITH_CPP11_ASYNC // Third, do the numerical integration for all reactions. - size_t grainSize = min( nvPools, 1 + (nvPools / numThreads_ ) ); - size_t nWorkers = nvPools / grainSize; + size_t grainSize = max( (size_t)1, min( nvPools, nvPools / numThreads_)); + size_t nWorkers = std::max(1, (int)(nvPools/grainSize) ); + + // Just to be sure. Its not very costly computation. + while( nWorkers * grainSize < nvPools ) + grainSize += 1; if( 1 == nWorkers || 1 == nvPools ) { if( numThreads_ > 1 ) { -#ifndef NDEBUG - cout << "Debug: Reset to 1 threads " << endl; -#endif + cerr << "Warn: Not enough voxels or threads. Reverting to serial mode. " << endl; numThreads_ = 1; } - for ( size_t i = 0; i < nvPools; i++ ) + for ( unsigned int i = 0; i < nvPools; i++ ) pools_[i].advance( p ); } else @@ -540,15 +565,19 @@ void Ksolve::process( const Eref& e, ProcPtr p ) * Somewhat complicated computation to compute the number of threads. 1 * thread per (at least) voxel pool is ideal situation. *-----------------------------------------------------------------------------*/ - //cout << "Grain size " << grainSize << " Workers : " << nWorkers << endl; + vector<std::thread> vecThreads; + // cout << nWorkers << " grain size " << grainSize << endl; + + // lambdas are faster than std::bind for (size_t i = 0; i < nWorkers; i++) - parallel_advance( i * grainSize, (i+1) * grainSize, nWorkers, p ); - } -#else - for ( size_t i = 0; i < nvPools; i++ ) - pools_[i].advance( p ); -#endif + { + std::thread t( &Ksolve::advance_chunk, this, i*grainSize, (i+1)*grainSize, p ); + vecThreads.push_back( std::move(t) ); + } + for (auto &v : vecThreads ) + v.join(); + } // Assemble and send the integrated values off for the Dsolve. if ( dsolvePtr_ ) @@ -561,40 +590,24 @@ void Ksolve::process( const Eref& e, ProcPtr p ) getBlock( kvalues ); dsolvePtr_->setBlock( kvalues ); - // Now use the values in the Dsolve to update junction fluxes - // for diffusion, channels, and xreacs - dsolvePtr_->updateJunctions( p->dt ); + // Now use the values in the Dsolve to update junction fluxes + // for diffusion, channels, and xreacs + dsolvePtr_->updateJunctions( p->dt ); } + t1_ = high_resolution_clock::now(); + moose::addSolverProf( "Ksolve", duration_cast<duration<double>> (t1_ - t0_ ).count(), 1 ); } +void Ksolve::advance_pool( const size_t i, ProcPtr p ) +{ + pools_[i].advance(p); +} -#if PARALLELIZE_KSOLVE_WITH_CPP11_ASYNC -/** - * @brief Advance voxels pools using parallel Ksolve. - * - * @param begin - * @param end - * @param p - */ -void Ksolve::parallel_advance(int begin, int end, size_t nWorkers, ProcPtr p) +void Ksolve::advance_chunk( const size_t begin, const size_t end, ProcPtr p ) { - std::atomic<int> idx( begin ); - for (size_t cpu = 0; cpu != nWorkers; ++cpu) - { - std::async( std::launch::async - , [this, &idx, end, p]() { - for (;;) - { - int i = idx++; - if (i >= end) - break; - pools_[i].advance( p ); - } - } - ); - } + for (size_t i = begin; i < std::min(end, pools_.size() ); i++) + pools_[i].advance( p ); } -#endif void Ksolve::reinit( const Eref& e, ProcPtr p ) @@ -613,10 +626,8 @@ void Ksolve::reinit( const Eref& e, ProcPtr p ) return; } -#if PARALLELIZE_KSOLVE_WITH_CPP11_ASYNC if( 1 < getNumThreads( ) ) - cout << "Debug: User wants Ksolve with " << numThreads_ << " threads" << endl; -#endif + cout << "Info: Setting up ksolve with " << numThreads_ << " threads" << endl; } diff --git a/ksolve/Ksolve.h b/ksolve/Ksolve.h index c7f4af8a..f4b9327a 100644 --- a/ksolve/Ksolve.h +++ b/ksolve/Ksolve.h @@ -10,6 +10,10 @@ #ifndef _KSOLVE_H #define _KSOLVE_H +#include <chrono> + +using namespace std::chrono; + class Stoich; class Ksolve: public ZombiePoolInterface @@ -54,14 +58,13 @@ public: vector< double > getNvec( unsigned int voxel) const; void setNvec( unsigned int voxel, vector< double > vec ); -#if PARALLELIZE_KSOLVE_WITH_CPP11_ASYNC // Set number of threads to use (for deterministic case only). unsigned int getNumThreads( ) const; void setNumThreads( unsigned int x ); - // Parallel advance(). - void parallel_advance(int begin, int end, size_t nWorkers, ProcPtr p); -#endif + void advance_chunk( const size_t begin, const size_t end, ProcPtr p ); + + void advance_pool( const size_t i, ProcPtr p ); /** * This does a quick and dirty estimate of the timestep suitable @@ -139,12 +142,10 @@ private: double epsAbs_; double epsRel_; -#if PARALLELIZE_KSOLVE_WITH_CPP11_ASYNC /** * @brief Number of threads to use. Only applicable for deterministic case. */ unsigned int numThreads_; -#endif /** * Each VoxelPools entry handles all the pools in a single voxel. @@ -169,6 +170,14 @@ private: /// Pointer to diffusion solver ZombiePoolInterface* dsolvePtr_; + // Timing and benchmarking related variables. + size_t numSteps_ = 0; + + // Time taken in all process function in us. + double totalTime_ = 0.0; + + high_resolution_clock::time_point t0_, t1_; + }; #endif // _KSOLVE_H diff --git a/ksolve/OdeSystem.h b/ksolve/OdeSystem.h index 84e2c72f..6a3ede6d 100644 --- a/ksolve/OdeSystem.h +++ b/ksolve/OdeSystem.h @@ -37,8 +37,6 @@ class OdeSystem { double epsRel; // Relative error #if USE_BOOST_ODE - //BoostSys* pBoostSys; - BoostSys boostSys; size_t dimension; #endif /* ----- USE_BOOST_ODE ----- */ }; diff --git a/ksolve/VoxelPools.cpp b/ksolve/VoxelPools.cpp index 38c5e2e7..ea575304 100644 --- a/ksolve/VoxelPools.cpp +++ b/ksolve/VoxelPools.cpp @@ -31,7 +31,6 @@ using namespace boost::numeric; ////////////////////////////////////////////////////////////// // Class definitions -////////////////////////////////////////////////////////////// VoxelPools::VoxelPools() { @@ -67,6 +66,10 @@ void VoxelPools::reinit( double dt ) void VoxelPools::setStoich( Stoich* s, const OdeSystem* ode ) { stoichPtr_ = s; + absTol_ = ode->epsAbs; + relTol_ = ode->epsRel; + method_ = ode->method; + #ifdef USE_GSL if ( ode ) { @@ -76,12 +79,9 @@ void VoxelPools::setStoich( Stoich* s, const OdeSystem* ode ) driver_ = gsl_odeiv2_driver_alloc_y_new( &sys_, ode->gslStep, ode->initStepSize, - ode->epsAbs, ode->epsRel - ); + ode->epsAbs, ode->epsRel + ); } -#elif USE_BOOST_ODE - if( ode ) - sys_ = ode->boostSys; #endif VoxelPoolsBase::reinit(); } @@ -108,11 +108,6 @@ void VoxelPools::advance( const ProcInfo* p ) #elif USE_BOOST_ODE - - // NOTE: Make sure to assing vp to BoostSys vp. In next call, it will be used by - // updateRates func. Unlike gsl call, we can't pass extra void* to gslFunc. - VoxelPools* vp = reinterpret_cast< VoxelPools* >( sys_.params ); - sys_.vp = vp; /*----------------------------------------------------------------------------- NOTE: 04/21/2016 11:31:42 AM @@ -122,7 +117,7 @@ void VoxelPools::advance( const ProcInfo* p ) take away the constantness of double*. This probably makes the call bit cleaner. *-----------------------------------------------------------------------------*/ - vp->stoichPtr_->updateFuncs( &Svec()[0], p->currTime ); + stoichPtr_->updateFuncs( &Svec()[0], p->currTime ); /*----------------------------------------------------------------------------- * Using integrate function works with with default stepper type. @@ -134,10 +129,6 @@ void VoxelPools::advance( const ProcInfo* p ) *----------------------------------------------------------------------------- */ - double absTol = sys_.epsAbs; - double relTol = sys_.epsRel; - - /** * @brief Default step size for fixed size iterator. * FIXME/TODO: I am not sure if this is a right value to pick by default. May be @@ -146,78 +137,113 @@ void VoxelPools::advance( const ProcInfo* p ) */ const double fixedDt = 0.1; - if( sys_.method == "rk2" ) + if( method_ == "rk2" ) odeint::integrate_const( rk_midpoint_stepper_type_() - , sys_ , Svec() - , p->currTime - p->dt, p->currTime, std::min( p->dt, fixedDt ) - ); - else if( sys_.method == "rk4" ) + , [this](const vector_type_& dy, vector_type_& dydt, const double t) { + VoxelPools::evalRates(this, dy, dydt ); + } + , Svec() + , p->currTime - p->dt, p->currTime, std::min( p->dt, fixedDt ) + ); + else if( method_ == "rk4" ) odeint::integrate_const( rk4_stepper_type_() - , sys_ , Svec() - , p->currTime - p->dt, p->currTime, std::min( p->dt, fixedDt ) - ); - else if( sys_.method == "rk5") + , [this](const vector_type_& dy, vector_type_& dydt, const double t) { + VoxelPools::evalRates(this, dy, dydt ); + } + , Svec() + , p->currTime - p->dt, p->currTime, std::min( p->dt, fixedDt ) + ); + else if( method_ == "rk5") odeint::integrate_const( rk_karp_stepper_type_() - , sys_ , Svec() - , p->currTime - p->dt, p->currTime, std::min( p->dt, fixedDt ) - ); - else if( sys_.method == "rk5a") + , [this](const vector_type_& dy, vector_type_& dydt, const double t) { + VoxelPools::evalRates(this, dy, dydt ); + } + , Svec() + , p->currTime - p->dt, p->currTime, std::min( p->dt, fixedDt ) + ); + else if( method_ == "rk5a") odeint::integrate_adaptive( - odeint::make_controlled<rk_karp_stepper_type_>( absTol, relTol) - , sys_ - , Svec() - , p->currTime - p->dt - , p->currTime - , p->dt - ); - else if ("rk54" == sys_.method ) + odeint::make_controlled<rk_karp_stepper_type_>( absTol_, relTol_ ) + , [this](const vector_type_& dy, vector_type_& dydt, const double t) { + VoxelPools::evalRates(this, dy, dydt ); + } + , Svec() + , p->currTime - p->dt + , p->currTime + , p->dt + ); + else if ("rk54" == method_ ) odeint::integrate_const( rk_karp_stepper_type_() - , sys_ , Svec() - , p->currTime - p->dt, p->currTime, std::min( p->dt, fixedDt ) - ); - else if ("rk54a" == sys_.method ) + , [this](const vector_type_& dy, vector_type_& dydt, const double t) { + VoxelPools::evalRates(this, dy, dydt ); + } + , Svec() + , p->currTime - p->dt, p->currTime, std::min( p->dt, fixedDt ) + ); + else if ("rk54a" == method_ ) odeint::integrate_adaptive( - odeint::make_controlled<rk_karp_stepper_type_>( absTol, relTol ) - , sys_, Svec() - , p->currTime - p->dt - , p->currTime - , p->dt - ); - else if ("rk5" == sys_.method ) + odeint::make_controlled<rk_karp_stepper_type_>( absTol_, relTol_ ) + , [this](const vector_type_& dy, vector_type_& dydt, const double t) { + VoxelPools::evalRates(this, dy, dydt ); + } + , Svec() + , p->currTime - p->dt + , p->currTime + , p->dt + ); + else if ("rk5" == method_ ) odeint::integrate_const( rk_dopri_stepper_type_() - , sys_ , Svec() - , p->currTime - p->dt, p->currTime, std::min( p->dt, fixedDt ) - ); - else if ("rk5a" == sys_.method ) + , [this](const vector_type_& dy, vector_type_& dydt, const double t) { + VoxelPools::evalRates(this, dy, dydt ); + } + , Svec() + , p->currTime - p->dt + , p->currTime + , std::min( p->dt, fixedDt ) + ); + else if ("rk5a" == method_ ) odeint::integrate_adaptive( - odeint::make_controlled<rk_dopri_stepper_type_>( absTol, relTol ) - , sys_, Svec() - , p->currTime - p->dt - , p->currTime - , p->dt - ); - else if( sys_.method == "rk8" ) + odeint::make_controlled<rk_dopri_stepper_type_>( absTol_, relTol_ ) + , [this](const vector_type_& dy, vector_type_& dydt, const double t) { + VoxelPools::evalRates(this, dy, dydt ); + } + , Svec() + , p->currTime - p->dt + , p->currTime + , p->dt + ); + else if( method_ == "rk8" ) odeint::integrate_const( rk_felhberg_stepper_type_() - , sys_ , Svec() - , p->currTime - p->dt, p->currTime, std::min( p->dt, fixedDt ) - ); - else if( sys_.method == "rk8a" ) + , [this](const vector_type_& dy, vector_type_& dydt, const double t) { + VoxelPools::evalRates(this, dy, dydt ); + } + , Svec() + , p->currTime - p->dt, p->currTime, std::min( p->dt, fixedDt ) + ); + else if( method_ == "rk8a" ) odeint::integrate_adaptive( - odeint::make_controlled<rk_felhberg_stepper_type_>( absTol, relTol ) - , sys_, Svec() - , p->currTime - p->dt - , p->currTime - , p->dt - ); + odeint::make_controlled<rk_felhberg_stepper_type_>( absTol_, relTol_ ) + , [this](const vector_type_& dy, vector_type_& dydt, const double t) { + VoxelPools::evalRates(this, dy, dydt ); + } + , Svec() + , p->currTime - p->dt + , p->currTime + , p->dt + ); else odeint::integrate_adaptive( - odeint::make_controlled<rk_karp_stepper_type_>( absTol, relTol ) - , sys_, Svec() - , p->currTime - p->dt - , p->currTime - , p->dt - ); + odeint::make_controlled<rk_karp_stepper_type_>( absTol_, relTol_ ) + , [this](const vector_type_& dy, vector_type_& dydt, const double t) { + VoxelPools::evalRates(this, dy, dydt ); + } + , Svec() + , p->currTime - p->dt + , p->currTime + , p->dt + ); + #endif if ( !stoichPtr_->getAllowNegative() ) // clean out negatives { @@ -266,13 +292,13 @@ int VoxelPools::gslFunc( double t, const double* y, double *dydt, #endif } -#elif USE_BOOST_ODE -void VoxelPools::evalRates( - const vector_type_& y, vector_type_& dydt, const double t, VoxelPools* vp -) +#elif USE_BOOST_ODE // NOT GSL + +void VoxelPools::evalRates( VoxelPools* vp, const vector_type_& y, vector_type_& dydt ) { vp->updateRates( &y[0], &dydt[0] ); } + #endif /////////////////////////////////////////////////////////////////////// @@ -287,8 +313,10 @@ void VoxelPools::updateAllRateTerms( const vector< RateTerm* >& rates, delete( rates_[i] ); rates_.resize( rates.size() ); + for ( unsigned int i = 0; i < numCoreRates; ++i ) rates_[i] = rates[i]->copyWithVolScaling( getVolume(), 1, 1 ); + for ( unsigned int i = numCoreRates; i < rates.size(); ++i ) { rates_[i] = rates[i]->copyWithVolScaling( getVolume(), @@ -337,7 +365,7 @@ void VoxelPools::updateRates( const double* s, double* yprime ) const } for (unsigned int i = 0; i < totVar; ++i) - *yprime++ = N.computeRowRate( i , v ); + *yprime++ = N.computeRowRate( i, v ); for (unsigned int i = 0; i < totInvar ; ++i) *yprime++ = 0.0; } diff --git a/ksolve/VoxelPools.h b/ksolve/VoxelPools.h index a8f0b4ae..a39b151c 100644 --- a/ksolve/VoxelPools.h +++ b/ksolve/VoxelPools.h @@ -1,6 +1,5 @@ /********************************************************************** ** This program is part of 'MOOSE', the -#include <boost/numeric/odeint.hpp> ** Messaging Object Oriented Simulation Environment. ** Copyright (C) 2003-2014 Upinder S. Bhalla. and NCBS ** It is made available under the terms of the @@ -55,11 +54,7 @@ public: #ifdef USE_GSL /* ----- not USE_BOOST_ODE ----- */ static int gslFunc( double t, const double* y, double *dydt, void* params); #elif USE_BOOST_ODE - static void evalRates( const vector_type_& y - , vector_type_& dydt - , const double t - , VoxelPools* vp - ); + static void evalRates( VoxelPools* vp, const vector_type_& y, vector_type_& dydt ); #endif /* ----- not USE_BOOST_ODE ----- */ ////////////////////////////////////////////////////////////////// @@ -102,10 +97,12 @@ private: #ifdef USE_GSL gsl_odeiv2_driver* driver_; gsl_odeiv2_system sys_; -#elif USE_BOOST_ODE - BoostSys sys_; #endif + double absTol_; + double relTol_; + string method_; + }; #endif // _VOXEL_POOLS_H diff --git a/ksolve/ZombiePoolInterface.cpp b/ksolve/ZombiePoolInterface.cpp index b75254d2..1df99ae2 100644 --- a/ksolve/ZombiePoolInterface.cpp +++ b/ksolve/ZombiePoolInterface.cpp @@ -30,8 +30,8 @@ #include "../mesh/ChemCompt.h" ZombiePoolInterface::ZombiePoolInterface() - : stoich_(), compartment_(), - isBuilt_( false ) + : stoich_(), compartment_(), + isBuilt_( false ) {;} void ZombiePoolInterface::updateJunctions( double dt ) @@ -43,21 +43,24 @@ void ZombiePoolInterface::setPrev() Id ZombiePoolInterface::getCompartment() const { - return compartment_; + return compartment_; } void ZombiePoolInterface::setCompartment( Id compt ) { - isBuilt_ = false; // We will have to now rebuild the whole thing. - if ( compt.element()->cinfo()->isA( "ChemCompt" ) ) { - compartment_ = compt; - vector< double > vols = - Field< vector < double > >::get( compt, "voxelVolume" ); - if ( vols.size() > 0 ) { - setNumAllVoxels( vols.size() ); - for ( unsigned int i = 0; i < vols.size(); ++i ) { - pools(i)->setVolume( vols[i] ); - } - } - } + isBuilt_ = false; // We will have to now rebuild the whole thing. + if ( compt.element()->cinfo()->isA( "ChemCompt" ) ) + { + compartment_ = compt; + vector< double > vols = + Field< vector < double > >::get( compt, "voxelVolume" ); + if ( vols.size() > 0 ) + { + setNumAllVoxels( vols.size() ); + for ( unsigned int i = 0; i < vols.size(); ++i ) + { + pools(i)->setVolume( vols[i] ); + } + } + } } diff --git a/ksolve/ZombiePoolInterface.h b/ksolve/ZombiePoolInterface.h index fe5bb1d8..70d49954 100644 --- a/ksolve/ZombiePoolInterface.h +++ b/ksolve/ZombiePoolInterface.h @@ -18,112 +18,113 @@ */ class ZombiePoolInterface { - public: - ZombiePoolInterface(); - - /// Set initial # of molecules in given pool and voxel. Bdry cond. - virtual void setNinit( const Eref& e, double val ) = 0; - /// get initial # of molecules in given pool and voxel. Bdry cond. - virtual double getNinit( const Eref& e ) const = 0; - - /// Set # of molecules in given pool and voxel. Varies with time. - virtual void setN( const Eref& e, double val ) = 0; - /// Get # of molecules in given pool and voxel. Varies with time. - virtual double getN( const Eref& e ) const = 0; - - /// Diffusion constant: Only one per pool, voxel number is ignored. - virtual void setDiffConst( const Eref& e, double val ) = 0; - /// Diffusion constant: Only one per pool, voxel number is ignored. - virtual double getDiffConst( const Eref& e ) const = 0; - - /// Motor constant: Only one per pool, voxel number is ignored. - /// Used only in Dsolves, so here I put in a dummy. - virtual void setMotorConst( const Eref& e, double val ) - {;} - - /// Specifies number of pools (species) handled by system. - virtual void setNumPools( unsigned int num ) = 0; - /// gets number of pools (species) handled by system. - virtual unsigned int getNumPools() const = 0; - - /// Assign number of voxels (size of pools_ vector ) - virtual void setNumAllVoxels( unsigned int numVoxels ) = 0; - /// Number of voxels here. pools_.size() == getNumLocalVoxels - virtual unsigned int getNumLocalVoxels() const = 0; - /// Return a pointer to the specified VoxelPool. - virtual VoxelPoolsBase* pools( unsigned int i ) = 0; - - /// Return volume of voxel i. - virtual double volume( unsigned int i ) const = 0; - - /** - * Gets block of data. The first 4 entries are passed in - * on the 'values' vector: the start voxel, numVoxels, - * start pool#, numPools. - * These are followed by numVoxels * numPools of data values - * which are filled in by the function. - * We assert that the entire requested block is present in - * this ZombiePoolInterface. - * The block is organized as an array of arrays of voxels; - * values[pool#][voxel#] - * - * Note that numVoxels and numPools are the number in the current - * block, not the upper limit of the block. So - * values.size() == 4 + numPools * numVoxels. - */ - virtual void getBlock( vector< double >& values ) const = 0; - - /** - * Sets block of data. The first 4 entries - * on the 'values' vector are the start voxel, numVoxels, - * start pool#, numPools. These are - * followed by numVoxels * numPools of data values. - */ - virtual void setBlock( const vector< double >& values ) = 0; - - /** - * Informs the ZPI about the stoich, used during subsequent - * computations. - * Called to wrap up the model building. The Stoich - * does this call after it has set up its own path. - */ - virtual void setStoich( Id stoich ) = 0; - - /// Assigns the diffusion solver. Used by the reac solvers - virtual void setDsolve( Id dsolve ) = 0; - - /// Assigns compartment. - virtual void setCompartment( Id compartment ); - Id getCompartment() const; - - /// Used for telling Dsolver to handle all ops across Junctions - virtual void updateJunctions( double dt ); - /// Used to tell Dsolver to assign 'prev' values. - virtual void setPrev(); - /** - * Informs the solver that the rate terms or volumes have changed - * and that the parameters must be updated. - * The index specifies which rateTerm to change, and if it is - * ~0U it means update all of them. - */ - virtual void updateRateTerms( unsigned int index = ~0U ) = 0; - - /// Return pool index, using Stoich ptr to do lookup. - virtual unsigned int getPoolIndex( const Eref& er ) const = 0; - - ////////////////////////////////////////////////////////////// - protected: - /** - * Stoich is the class that sets up the reaction system and - * manages the stoichiometry matrix - */ - Id stoich_; - - /// Id of Chem compartment used to figure out volumes of voxels. - Id compartment_; - - /// Flag: True when solver setup has been completed. - bool isBuilt_; +public: + ZombiePoolInterface(); + + /// Set initial # of molecules in given pool and voxel. Bdry cond. + virtual void setNinit( const Eref& e, double val ) = 0; + /// get initial # of molecules in given pool and voxel. Bdry cond. + virtual double getNinit( const Eref& e ) const = 0; + + /// Set # of molecules in given pool and voxel. Varies with time. + virtual void setN( const Eref& e, double val ) = 0; + /// Get # of molecules in given pool and voxel. Varies with time. + virtual double getN( const Eref& e ) const = 0; + + /// Diffusion constant: Only one per pool, voxel number is ignored. + virtual void setDiffConst( const Eref& e, double val ) = 0; + /// Diffusion constant: Only one per pool, voxel number is ignored. + virtual double getDiffConst( const Eref& e ) const = 0; + + /// Motor constant: Only one per pool, voxel number is ignored. + /// Used only in Dsolves, so here I put in a dummy. + virtual void setMotorConst( const Eref& e, double val ) + {;} + + /// Specifies number of pools (species) handled by system. + virtual void setNumPools( unsigned int num ) = 0; + /// gets number of pools (species) handled by system. + virtual unsigned int getNumPools() const = 0; + + /// Assign number of voxels (size of pools_ vector ) + virtual void setNumAllVoxels( unsigned int numVoxels ) = 0; + /// Number of voxels here. pools_.size() == getNumLocalVoxels + virtual unsigned int getNumLocalVoxels() const = 0; + /// Return a pointer to the specified VoxelPool. + virtual VoxelPoolsBase* pools( unsigned int i ) = 0; + + /// Return volume of voxel i. + virtual double volume( unsigned int i ) const = 0; + + /** + * Gets block of data. The first 4 entries are passed in + * on the 'values' vector: the start voxel, numVoxels, + * start pool#, numPools. + * These are followed by numVoxels * numPools of data values + * which are filled in by the function. + * We assert that the entire requested block is present in + * this ZombiePoolInterface. + * The block is organized as an array of arrays of voxels; + * values[pool#][voxel#] + * + * Note that numVoxels and numPools are the number in the current + * block, not the upper limit of the block. So + * values.size() == 4 + numPools * numVoxels. + */ + virtual void getBlock( vector< double >& values ) const = 0; + + /** + * Sets block of data. The first 4 entries + * on the 'values' vector are the start voxel, numVoxels, + * start pool#, numPools. These are + * followed by numVoxels * numPools of data values. + */ + virtual void setBlock( const vector< double >& values ) = 0; + + /** + * Informs the ZPI about the stoich, used during subsequent + * computations. + * Called to wrap up the model building. The Stoich + * does this call after it has set up its own path. + */ + virtual void setStoich( Id stoich ) = 0; + + /// Assigns the diffusion solver. Used by the reac solvers + virtual void setDsolve( Id dsolve ) = 0; + + /// Assigns compartment. + virtual void setCompartment( Id compartment ); + Id getCompartment() const; + + /// Used for telling Dsolver to handle all ops across Junctions + virtual void updateJunctions( double dt ); + + /// Used to tell Dsolver to assign 'prev' values. + virtual void setPrev(); + /** + * Informs the solver that the rate terms or volumes have changed + * and that the parameters must be updated. + * The index specifies which rateTerm to change, and if it is + * ~0U it means update all of them. + */ + virtual void updateRateTerms( unsigned int index = ~0U ) = 0; + + /// Return pool index, using Stoich ptr to do lookup. + virtual unsigned int getPoolIndex( const Eref& er ) const = 0; + + ////////////////////////////////////////////////////////////// +protected: + /** + * Stoich is the class that sets up the reaction system and + * manages the stoichiometry matrix + */ + Id stoich_; + + /// Id of Chem compartment used to figure out volumes of voxels. + Id compartment_; + + /// Flag: True when solver setup has been completed. + bool isBuilt_; }; -#endif // _ZOMBIE_POOL_INTERFACE_H +#endif // _ZOMBIE_POOL_INTERFACE_H diff --git a/mesh/CylMesh.cpp b/mesh/CylMesh.cpp index 82cd895f..9fc8352e 100644 --- a/mesh/CylMesh.cpp +++ b/mesh/CylMesh.cpp @@ -26,135 +26,135 @@ #include "../utility/numutil.h" const Cinfo* CylMesh::initCinfo() { - ////////////////////////////////////////////////////////////// - // Field Definitions - ////////////////////////////////////////////////////////////// - static ElementValueFinfo< CylMesh, double > x0( - "x0", - "x coord of one end", - &CylMesh::setX0, - &CylMesh::getX0 - ); - static ElementValueFinfo< CylMesh, double > y0( - "y0", - "y coord of one end", - &CylMesh::setY0, - &CylMesh::getY0 - ); - static ElementValueFinfo< CylMesh, double > z0( - "z0", - "z coord of one end", - &CylMesh::setZ0, - &CylMesh::getZ0 - ); - static ElementValueFinfo< CylMesh, double > r0( - "r0", - "Radius of one end", - &CylMesh::setR0, - &CylMesh::getR0 - ); - static ElementValueFinfo< CylMesh, double > x1( - "x1", - "x coord of other end", - &CylMesh::setX1, - &CylMesh::getX1 - ); - static ElementValueFinfo< CylMesh, double > y1( - "y1", - "y coord of other end", - &CylMesh::setY1, - &CylMesh::getY1 - ); - static ElementValueFinfo< CylMesh, double > z1( - "z1", - "z coord of other end", - &CylMesh::setZ1, - &CylMesh::getZ1 - ); - static ElementValueFinfo< CylMesh, double > r1( - "r1", - "Radius of other end", - &CylMesh::setR1, - &CylMesh::getR1 - ); - static ElementValueFinfo< CylMesh, vector< double > > coords( - "coords", - "All the coords as a single vector: x0 y0 z0 x1 y1 z1 r0 r1 diffLength", - &CylMesh::setCoords, - &CylMesh::getCoords - ); - - static ElementValueFinfo< CylMesh, double > diffLength( - "diffLength", - "Length constant to use for subdivisions" - "The system will attempt to subdivide using compartments of" - "length diffLength on average. If the cylinder has different end" - "diameters r0 and r1, it will scale to smaller lengths" - "for the smaller diameter end and vice versa." - "Once the value is set it will recompute diffLength as " - "totLength/numEntries", - &CylMesh::setDiffLength, - &CylMesh::getDiffLength - ); - - static ReadOnlyValueFinfo< CylMesh, unsigned int > numDiffCompts( - "numDiffCompts", - "Number of diffusive compartments in model", - &CylMesh::innerGetNumEntries - ); - - static ReadOnlyValueFinfo< CylMesh, double > totLength( - "totLength", - "Total length of cylinder", - &CylMesh::getTotLength - ); - - ////////////////////////////////////////////////////////////// - // MsgDest Definitions - ////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////// - // Field Elements - ////////////////////////////////////////////////////////////// - - static Finfo* cylMeshFinfos[] = { - &x0, // Value - &y0, // Value - &z0, // Value - &r0, // Value - &x1, // Value - &y1, // Value - &z1, // Value - &r1, // Value - &diffLength, // Value - &coords, // Value - &numDiffCompts, // ReadOnlyValue - &totLength, // ReadOnlyValue - }; - - static string doc[] = - { - "Name", "CylMesh", - "Author", "Upi Bhalla", - "Description", "Chemical compartment with cylindrical geometry. " - "Defaults to a uniform cylinder of radius 1 micron, " - "length 100 microns, and voxel length 1 micron so there " - "are 100 voxels in the cylinder. " - "The cylinder can be given a linear taper, by assigning " - "different radii r0 and r1 to the two ends. ", - }; - static Dinfo< CylMesh > dinfo; - static Cinfo cylMeshCinfo ( - "CylMesh", - ChemCompt::initCinfo(), - cylMeshFinfos, - sizeof( cylMeshFinfos ) / sizeof ( Finfo* ), - &dinfo, - doc, + ////////////////////////////////////////////////////////////// + // Field Definitions + ////////////////////////////////////////////////////////////// + static ElementValueFinfo< CylMesh, double > x0( + "x0", + "x coord of one end", + &CylMesh::setX0, + &CylMesh::getX0 + ); + static ElementValueFinfo< CylMesh, double > y0( + "y0", + "y coord of one end", + &CylMesh::setY0, + &CylMesh::getY0 + ); + static ElementValueFinfo< CylMesh, double > z0( + "z0", + "z coord of one end", + &CylMesh::setZ0, + &CylMesh::getZ0 + ); + static ElementValueFinfo< CylMesh, double > r0( + "r0", + "Radius of one end", + &CylMesh::setR0, + &CylMesh::getR0 + ); + static ElementValueFinfo< CylMesh, double > x1( + "x1", + "x coord of other end", + &CylMesh::setX1, + &CylMesh::getX1 + ); + static ElementValueFinfo< CylMesh, double > y1( + "y1", + "y coord of other end", + &CylMesh::setY1, + &CylMesh::getY1 + ); + static ElementValueFinfo< CylMesh, double > z1( + "z1", + "z coord of other end", + &CylMesh::setZ1, + &CylMesh::getZ1 + ); + static ElementValueFinfo< CylMesh, double > r1( + "r1", + "Radius of other end", + &CylMesh::setR1, + &CylMesh::getR1 + ); + static ElementValueFinfo< CylMesh, vector< double > > coords( + "coords", + "All the coords as a single vector: x0 y0 z0 x1 y1 z1 r0 r1 diffLength", + &CylMesh::setCoords, + &CylMesh::getCoords + ); + + static ElementValueFinfo< CylMesh, double > diffLength( + "diffLength", + "Length constant to use for subdivisions" + "The system will attempt to subdivide using compartments of" + "length diffLength on average. If the cylinder has different end" + "diameters r0 and r1, it will scale to smaller lengths" + "for the smaller diameter end and vice versa." + "Once the value is set it will recompute diffLength as " + "totLength/numEntries", + &CylMesh::setDiffLength, + &CylMesh::getDiffLength + ); + + static ReadOnlyValueFinfo< CylMesh, unsigned int > numDiffCompts( + "numDiffCompts", + "Number of diffusive compartments in model", + &CylMesh::innerGetNumEntries + ); + + static ReadOnlyValueFinfo< CylMesh, double > totLength( + "totLength", + "Total length of cylinder", + &CylMesh::getTotLength + ); + + ////////////////////////////////////////////////////////////// + // MsgDest Definitions + ////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////// + // Field Elements + ////////////////////////////////////////////////////////////// + + static Finfo* cylMeshFinfos[] = { + &x0, // Value + &y0, // Value + &z0, // Value + &r0, // Value + &x1, // Value + &y1, // Value + &z1, // Value + &r1, // Value + &diffLength, // Value + &coords, // Value + &numDiffCompts, // ReadOnlyValue + &totLength, // ReadOnlyValue + }; + + static string doc[] = + { + "Name", "CylMesh", + "Author", "Upi Bhalla", + "Description", "Chemical compartment with cylindrical geometry. " + "Defaults to a uniform cylinder of radius 1 micron, " + "length 100 microns, and voxel length 1 micron so there " + "are 100 voxels in the cylinder. " + "The cylinder can be given a linear taper, by assigning " + "different radii r0 and r1 to the two ends. ", + }; + static Dinfo< CylMesh > dinfo; + static Cinfo cylMeshCinfo ( + "CylMesh", + ChemCompt::initCinfo(), + cylMeshFinfos, + sizeof( cylMeshFinfos ) / sizeof ( Finfo* ), + &dinfo, + doc, sizeof(doc)/sizeof(string) - ); + ); - return &cylMeshCinfo; + return &cylMeshCinfo; } ////////////////////////////////////////////////////////////// @@ -167,30 +167,30 @@ static const Cinfo* cylMeshCinfo = CylMesh::initCinfo(); // Class stuff. ////////////////////////////////////////////////////////////////// CylMesh::CylMesh() - : - numEntries_( 100 ), - useCaps_( 0 ), - isToroid_( false ), - x0_( 0.0 ), - y0_( 0.0 ), - z0_( 0.0 ), - x1_( 100.0e-6 ), - y1_( 0.0 ), - z1_( 0.0 ), - r0_( 1.0e-6 ), - r1_( 1.0e-6 ), - diffLength_( 1.0e-6 ), - surfaceGranularity_( 0.1 ), - totLen_( 100.0e-6 ), - rSlope_( 0.0 ), - lenSlope_( 0.0 ) + : + numEntries_( 100 ), + useCaps_( 0 ), + isToroid_( false ), + x0_( 0.0 ), + y0_( 0.0 ), + z0_( 0.0 ), + x1_( 100.0e-6 ), + y1_( 0.0 ), + z1_( 0.0 ), + r0_( 1.0e-6 ), + r1_( 1.0e-6 ), + diffLength_( 1.0e-6 ), + surfaceGranularity_( 0.1 ), + totLen_( 100.0e-6 ), + rSlope_( 0.0 ), + lenSlope_( 0.0 ) { - ; + ; } CylMesh::~CylMesh() { - ; + ; } ////////////////////////////////////////////////////////////////// @@ -204,213 +204,222 @@ CylMesh::~CylMesh() */ void CylMesh::updateCoords( const Eref& e, const vector< double >& concs ) { - double temp = sqrt( - ( x1_ - x0_ ) * ( x1_ - x0_ ) + - ( y1_ - y0_ ) * ( y1_ - y0_ ) + - ( z1_ - z0_ ) * ( z1_ - z0_ ) - ); - - if ( doubleEq( temp, 0.0 ) ) { - cout << "Error: CylMesh::updateCoords:\n" - "total length of compartment = 0 with these parameters\n"; - return; - } - totLen_ = temp; - - - temp = totLen_ / diffLength_; - if ( temp < 1.0 ) { - diffLength_ = totLen_; - numEntries_ = 1; - } else { - numEntries_ = static_cast< unsigned int >( round ( temp ) ); - diffLength_ = totLen_ / numEntries_; - } - rSlope_ = ( r1_ - r0_ ) / numEntries_; - lenSlope_ = diffLength_ * rSlope_ * 2 / ( r0_ + r1_ ); - - // dx2_[0] = diffLength_; - // dx2_[1] = diffLength_; - buildStencil(); - setChildConcs( e, concs, 0 ); + double temp = sqrt( + ( x1_ - x0_ ) * ( x1_ - x0_ ) + + ( y1_ - y0_ ) * ( y1_ - y0_ ) + + ( z1_ - z0_ ) * ( z1_ - z0_ ) + ); + + if ( doubleEq( temp, 0.0 ) ) { + cout << "Error: CylMesh::updateCoords:\n" + "total length of compartment = 0 with these parameters\n"; + return; + } + totLen_ = temp; + + temp = totLen_ / diffLength_; + if ( temp < 1.0 ) { + diffLength_ = totLen_; + numEntries_ = 1; + } else { + numEntries_ = static_cast< unsigned int >( round ( temp ) ); + diffLength_ = totLen_ / numEntries_; + } + rSlope_ = ( r1_ - r0_ ) / numEntries_; + lenSlope_ = diffLength_ * rSlope_ * 2 / ( r0_ + r1_ ); + + // dx2_[0] = diffLength_; + // dx2_[1] = diffLength_; + buildStencil(); + setChildConcs( e, concs, 0 ); } void CylMesh::setX0( const Eref& e, double v ) { - vector< double > childConcs; - getChildConcs( e, childConcs ); - x0_ = v; - updateCoords( e, childConcs ); + vector< double > childConcs; + getChildConcs( e, childConcs ); + x0_ = v; + updateCoords( e, childConcs ); } double CylMesh::getX0( const Eref& e ) const { - return x0_; + return x0_; } void CylMesh::setY0( const Eref& e, double v ) { - vector< double > childConcs; - getChildConcs( e, childConcs ); - y0_ = v; - updateCoords( e, childConcs ); + vector< double > childConcs; + getChildConcs( e, childConcs ); + y0_ = v; + updateCoords( e, childConcs ); } double CylMesh::getY0( const Eref& e ) const { - return y0_; + return y0_; } void CylMesh::setZ0( const Eref& e, double v ) { - vector< double > childConcs; - getChildConcs( e, childConcs ); - z0_ = v; - updateCoords( e, childConcs ); + vector< double > childConcs; + getChildConcs( e, childConcs ); + z0_ = v; + updateCoords( e, childConcs ); } double CylMesh::getZ0( const Eref& e ) const { - return z0_; + return z0_; } void CylMesh::setR0( const Eref& e, double v ) { - vector< double > childConcs; - getChildConcs( e, childConcs ); - r0_ = v; - updateCoords( e, childConcs ); + vector< double > childConcs; + getChildConcs( e, childConcs ); + r0_ = v; + updateCoords( e, childConcs ); } double CylMesh::getR0( const Eref& e ) const { - return r0_; + return r0_; } void CylMesh::setX1( const Eref& e, double v ) { - vector< double > childConcs; - getChildConcs( e, childConcs ); - x1_ = v; - updateCoords( e, childConcs ); + + size_t numVoxels = (v - x0_) / diffLength_; + if( numVoxels > SM_MAX_COLUMNS ) + { + cout << "Warn: Compartment is too big. With diffusion-length of " << diffLength_ + << " total " << numVoxels << " would be generated which is larger than maximum " + << SM_MAX_COLUMNS << " allowed. Ignoring .." << endl; + return; + } + + vector< double > childConcs; + getChildConcs( e, childConcs ); + x1_ = v; + updateCoords( e, childConcs ); } double CylMesh::getX1( const Eref& e ) const { - return x1_; + return x1_; } void CylMesh::setY1( const Eref& e, double v ) { - vector< double > childConcs; - getChildConcs( e, childConcs ); - y1_ = v; - updateCoords( e, childConcs ); + vector< double > childConcs; + getChildConcs( e, childConcs ); + y1_ = v; + updateCoords( e, childConcs ); } double CylMesh::getY1( const Eref& e ) const { - return y1_; + return y1_; } void CylMesh::setZ1( const Eref& e, double v ) { - vector< double > childConcs; - getChildConcs( e, childConcs ); - z1_ = v; - updateCoords( e, childConcs ); + vector< double > childConcs; + getChildConcs( e, childConcs ); + z1_ = v; + updateCoords( e, childConcs ); } double CylMesh::getZ1( const Eref& e ) const { - return z1_; + return z1_; } void CylMesh::setR1( const Eref& e, double v ) { - vector< double > childConcs; - getChildConcs( e, childConcs ); - r1_ = v; - updateCoords( e, childConcs ); + vector< double > childConcs; + getChildConcs( e, childConcs ); + r1_ = v; + updateCoords( e, childConcs ); } double CylMesh::getR1( const Eref& e ) const { - return r1_; + return r1_; } void CylMesh::innerSetCoords( const Eref& e, const vector< double >& v ) { - vector< double > childConcs; - getChildConcs( e, childConcs ); - x0_ = v[0]; - y0_ = v[1]; - z0_ = v[2]; + vector< double > childConcs; + getChildConcs( e, childConcs ); + x0_ = v[0]; + y0_ = v[1]; + z0_ = v[2]; - x1_ = v[3]; - y1_ = v[4]; - z1_ = v[5]; + x1_ = v[3]; + y1_ = v[4]; + z1_ = v[5]; - r0_ = v[6]; - r1_ = v[7]; + r0_ = v[6]; + r1_ = v[7]; - diffLength_ = v[8]; + diffLength_ = v[8]; - updateCoords( e, childConcs ); + updateCoords( e, childConcs ); } void CylMesh::setCoords( const Eref& e, vector< double > v ) { - if ( v.size() < 9 ) { - cout << "CylMesh::setCoords: Warning: size of argument vec should be >= 9, was " << v.size() << endl; - } - innerSetCoords( e, v ); - transmitChange( e ); + if ( v.size() < 9 ) { + cout << "CylMesh::setCoords: Warning: size of argument vec should be >= 9, was " << v.size() << endl; + } + innerSetCoords( e, v ); + transmitChange( e ); } vector< double > CylMesh::getCoords( const Eref& e ) const { - vector< double > ret( 9 ); + vector< double > ret( 9 ); - ret[0] = x0_; - ret[1] = y0_; - ret[2] = z0_; + ret[0] = x0_; + ret[1] = y0_; + ret[2] = z0_; - ret[3] = x1_; - ret[4] = y1_; - ret[5] = z1_; + ret[3] = x1_; + ret[4] = y1_; + ret[5] = z1_; - ret[6] = r0_; - ret[7] = r1_; + ret[6] = r0_; + ret[7] = r1_; - ret[8] = diffLength_; - return ret; + ret[8] = diffLength_; + return ret; } void CylMesh::setDiffLength( const Eref& e, double v ) { - vector< double > childConcs; - getChildConcs( e, childConcs ); - diffLength_ = v; - updateCoords( e, childConcs ); + vector< double > childConcs; + getChildConcs( e, childConcs ); + diffLength_ = v; + updateCoords( e, childConcs ); } double CylMesh::getDiffLength( const Eref& e ) const { - return diffLength_; + return diffLength_; } double CylMesh::getTotLength() const { - return totLen_; + return totLen_; } unsigned int CylMesh::innerGetDimensions() const { - return 3; + return 3; } ////////////////////////////////////////////////////////////////// @@ -420,16 +429,16 @@ unsigned int CylMesh::innerGetDimensions() const /// Virtual function to return MeshType of specified entry. unsigned int CylMesh::getMeshType( unsigned int fid ) const { - if ( !isToroid_ && useCaps_ && ( fid == 0 || fid == numEntries_ - 1 ) ) - return SPHERE_SHELL_SEG; + if ( !isToroid_ && useCaps_ && ( fid == 0 || fid == numEntries_ - 1 ) ) + return SPHERE_SHELL_SEG; - return CYL; + return CYL; } /// Virtual function to return dimensions of specified entry. unsigned int CylMesh::getMeshDimensions( unsigned int fid ) const { - return 3; + return 3; } /** @@ -476,103 +485,103 @@ unsigned int CylMesh::getMeshDimensions( unsigned int fid ) const /// Virtual function to return volume of mesh Entry. double CylMesh::getMeshEntryVolume( unsigned int fid ) const { - double len0 = diffLength_ * 2 * r0_ / ( r0_ + r1_ ); + double len0 = diffLength_ * 2 * r0_ / ( r0_ + r1_ ); - double ri = r0_ + (fid + 0.5) * rSlope_; - double leni = len0 + ( fid + 0.5 ) * lenSlope_; + double ri = r0_ + (fid + 0.5) * rSlope_; + double leni = len0 + ( fid + 0.5 ) * lenSlope_; - return leni * ri * ri * PI; + return leni * ri * ri * PI; } /// Virtual function to return coords of mesh Entry. /// For Cylindrical mesh, coords are x1y1z1 x2y2z2 r0 r1 phi0 phi1 vector< double > CylMesh::getCoordinates( unsigned int fid ) const { - vector< double > ret(10, 0.0); - double len0 = diffLength_ * 2 * r0_ / ( r0_ + r1_ ); - // double len0 = diffLength_ * 2 * ( r0_ + rSlope_ / 0.5) / ( r0_ + r1_ ); - double lenStart = len0 + lenSlope_ * 0.5; + vector< double > ret(10, 0.0); + double len0 = diffLength_ * 2 * r0_ / ( r0_ + r1_ ); + // double len0 = diffLength_ * 2 * ( r0_ + rSlope_ / 0.5) / ( r0_ + r1_ ); + double lenStart = len0 + lenSlope_ * 0.5; - double axialStart = fid * lenStart + ( ( fid * (fid - 1 ) )/2 ) * lenSlope_; - // fid * totLen_/numEntries_ + (fid - frac ) * lenSlope_; - double axialEnd = - (fid + 1) * lenStart + ( ( fid * (fid + 1 ) )/2 ) * lenSlope_; - // (fid + 1) * totLen_/numEntries_ + (fid - frac + 1.0) * lenSlope_; + double axialStart = fid * lenStart + ( ( fid * (fid - 1 ) )/2 ) * lenSlope_; + // fid * totLen_/numEntries_ + (fid - frac ) * lenSlope_; + double axialEnd = + (fid + 1) * lenStart + ( ( fid * (fid + 1 ) )/2 ) * lenSlope_; + // (fid + 1) * totLen_/numEntries_ + (fid - frac + 1.0) * lenSlope_; - ret[0] = x0_ + (x1_ - x0_ ) * axialStart/totLen_; - ret[1] = y0_ + (y1_ - y0_ ) * axialStart/totLen_; - ret[2] = z0_ + (z1_ - z0_ ) * axialStart/totLen_; + ret[0] = x0_ + (x1_ - x0_ ) * axialStart/totLen_; + ret[1] = y0_ + (y1_ - y0_ ) * axialStart/totLen_; + ret[2] = z0_ + (z1_ - z0_ ) * axialStart/totLen_; - ret[3] = x0_ + (x1_ - x0_ ) * axialEnd/totLen_; - ret[4] = y0_ + (y1_ - y0_ ) * axialEnd/totLen_; - ret[5] = z0_ + (z1_ - z0_ ) * axialEnd/totLen_; + ret[3] = x0_ + (x1_ - x0_ ) * axialEnd/totLen_; + ret[4] = y0_ + (y1_ - y0_ ) * axialEnd/totLen_; + ret[5] = z0_ + (z1_ - z0_ ) * axialEnd/totLen_; - ret[6] = r0_ + fid * rSlope_; - ret[7] = r0_ + (fid + 1.0) * rSlope_; + ret[6] = r0_ + fid * rSlope_; + ret[7] = r0_ + (fid + 1.0) * rSlope_; - ret[8] = 0; - ret[9] = 0; + ret[8] = 0; + ret[9] = 0; - return ret; + return ret; } /// Virtual function to return diffusion X-section area for each neighbor vector< double > CylMesh::getDiffusionArea( unsigned int fid ) const { - if ( numEntries_ <= 1 ) - return vector< double >( 0 ); - - double rlow = r0_ + fid * rSlope_; - double rhigh = r0_ + (fid + 1.0) * rSlope_; - - if ( fid == 0 ) { - if ( isToroid_ ) { - vector < double > ret( 2 ); - ret[0] = rlow * rlow * PI; - ret[1] = rhigh * rhigh * PI; - return ret; - } else { - return vector < double >( 1, rhigh * rhigh * PI ); - } - } - - if ( fid == (numEntries_ - 1 ) ) { - if ( isToroid_ ) { - vector < double > ret( 2 ); - ret[0] = rlow * rlow * PI; - ret[1] = r0_ * r0_ * PI; // Wrapping around - return ret; - } else { - return vector < double >( 1, rlow * rlow * PI ); - } - } - vector< double > ret( 2 ); - ret[0] = rlow * rlow * PI; - ret[1] = rhigh * rhigh * PI; - return ret; + if ( numEntries_ <= 1 ) + return vector< double >( 0 ); + + double rlow = r0_ + fid * rSlope_; + double rhigh = r0_ + (fid + 1.0) * rSlope_; + + if ( fid == 0 ) { + if ( isToroid_ ) { + vector < double > ret( 2 ); + ret[0] = rlow * rlow * PI; + ret[1] = rhigh * rhigh * PI; + return ret; + } else { + return vector < double >( 1, rhigh * rhigh * PI ); + } + } + + if ( fid == (numEntries_ - 1 ) ) { + if ( isToroid_ ) { + vector < double > ret( 2 ); + ret[0] = rlow * rlow * PI; + ret[1] = r0_ * r0_ * PI; // Wrapping around + return ret; + } else { + return vector < double >( 1, rlow * rlow * PI ); + } + } + vector< double > ret( 2 ); + ret[0] = rlow * rlow * PI; + ret[1] = rhigh * rhigh * PI; + return ret; } /// Virtual function to return scale factor for diffusion. 1 here. vector< double > CylMesh::getDiffusionScaling( unsigned int fid ) const { - if ( numEntries_ <= 1 ) - return vector< double >( 0 ); + if ( numEntries_ <= 1 ) + return vector< double >( 0 ); - if ( !isToroid_ && ( fid == 0 || fid == (numEntries_ - 1) ) ) - return vector< double >( 1, 1.0 ); + if ( !isToroid_ && ( fid == 0 || fid == (numEntries_ - 1) ) ) + return vector< double >( 1, 1.0 ); - return vector< double >( 2, 1.0 ); + return vector< double >( 2, 1.0 ); } /// Virtual function to return volume of mesh Entry, including /// for diffusively coupled voxels from other solvers. double CylMesh::extendedMeshEntryVolume( unsigned int fid ) const { - if ( fid < numEntries_ ) { - return getMeshEntryVolume( fid ); - } else { - return MeshCompt::extendedMeshEntryVolume( fid - numEntries_ ); - } + if ( fid < numEntries_ ) { + return getMeshEntryVolume( fid ); + } else { + return MeshCompt::extendedMeshEntryVolume( fid - numEntries_ ); + } } ////////////////////////////////////////////////////////////////// @@ -581,31 +590,31 @@ double CylMesh::extendedMeshEntryVolume( unsigned int fid ) const /// More inherited virtual funcs: request comes in for mesh stats void CylMesh::innerHandleRequestMeshStats( const Eref& e, - const SrcFinfo2< unsigned int, vector< double > >* meshStatsFinfo - ) + const SrcFinfo2< unsigned int, vector< double > >* meshStatsFinfo + ) { - vector< double > ret( vGetEntireVolume() / numEntries_ ,1 ); - meshStatsFinfo->send( e, 1, ret ); + vector< double > ret( vGetEntireVolume() / numEntries_ ,1 ); + meshStatsFinfo->send( e, 1, ret ); } void CylMesh::innerHandleNodeInfo( - const Eref& e, - unsigned int numNodes, unsigned int numThreads ) + const Eref& e, + unsigned int numNodes, unsigned int numThreads ) { - /* - unsigned int numEntries = numEntries_; - vector< double > vols( numEntries, volume_ / numEntries ); - vector< unsigned int > localEntries( numEntries ); - vector< vector< unsigned int > > outgoingEntries; - vector< vector< unsigned int > > incomingEntries; - */ - /* - double oldvol = getMeshEntryVolume( 0 ); - meshSplit()->send( e, - oldvol, - vols, localEntries, - outgoingEntries, incomingEntries ); - */ + /* + unsigned int numEntries = numEntries_; + vector< double > vols( numEntries, volume_ / numEntries ); + vector< unsigned int > localEntries( numEntries ); + vector< vector< unsigned int > > outgoingEntries; + vector< vector< unsigned int > > incomingEntries; + */ + /* + double oldvol = getMeshEntryVolume( 0 ); + meshSplit()->send( e, + oldvol, + vols, localEntries, + outgoingEntries, incomingEntries ); + */ } ////////////////////////////////////////////////////////////////// @@ -614,7 +623,7 @@ void CylMesh::innerHandleNodeInfo( */ unsigned int CylMesh::innerGetNumEntries() const { - return numEntries_; + return numEntries_; } /** @@ -622,116 +631,116 @@ unsigned int CylMesh::innerGetNumEntries() const */ void CylMesh::innerSetNumEntries( unsigned int n ) { - static const unsigned int WayTooLarge = 1000000; - if ( n == 0 || n > WayTooLarge ) { - cout << "Warning: CylMesh::innerSetNumEntries( " << n << - " ): out of range\n"; - return; - } - assert( n > 0 ); - numEntries_ = n; - diffLength_ = totLen_ / n; - rSlope_ = ( r1_ - r0_ ) / numEntries_; - lenSlope_ = diffLength_ * rSlope_ * 2 / ( r0_ + r1_ ); - - buildStencil(); + static const unsigned int WayTooLarge = 1000000; + if ( n == 0 || n > WayTooLarge ) { + cout << "Warning: CylMesh::innerSetNumEntries( " << n << + " ): out of range\n"; + return; + } + assert( n > 0 ); + numEntries_ = n; + diffLength_ = totLen_ / n; + rSlope_ = ( r1_ - r0_ ) / numEntries_; + lenSlope_ = diffLength_ * rSlope_ * 2 / ( r0_ + r1_ ); + + buildStencil(); } void CylMesh::innerBuildDefaultMesh( const Eref& e, - double volume, unsigned int numEntries ) + double volume, unsigned int numEntries ) { - /// Single voxel cylinder with diameter = length. - /// vol = volume = pi.r^2.len. - /// So len = 2r, volume = pi*r^2*2r = 2pi*r^3 so r = (volume/2pi)^(1/3) - double r = pow( ( volume / ( PI * 2 ) ), 1.0 / 3 ); - vector< double > coords( 9, 0 ); - coords[3] = 2 * r; - coords[6] = r; - coords[7] = r; - coords[8] = 2 * r / numEntries; - setCoords( e, coords ); + /// Single voxel cylinder with diameter = length. + /// vol = volume = pi.r^2.len. + /// So len = 2r, volume = pi*r^2*2r = 2pi*r^3 so r = (volume/2pi)^(1/3) + double r = pow( ( volume / ( PI * 2 ) ), 1.0 / 3 ); + vector< double > coords( 9, 0 ); + coords[3] = 2 * r; + coords[6] = r; + coords[7] = r; + coords[8] = 2 * r / numEntries; + setCoords( e, coords ); } vector< unsigned int > CylMesh::getParentVoxel() const { - vector< unsigned int > ret( numEntries_ ); - if ( numEntries_ > 0 ) - ret[0] = static_cast< unsigned int >( -1 ); - for (unsigned int i = 1; i < numEntries_; ++i ) - ret[i] = i-1; + vector< unsigned int > ret( numEntries_ ); + if ( numEntries_ > 0 ) + ret[0] = static_cast< unsigned int >( -1 ); + for (unsigned int i = 1; i < numEntries_; ++i ) + ret[i] = i-1; - return ret; + return ret; } const vector< double >& CylMesh::vGetVoxelVolume() const { - static vector< double > vol; - vol.resize( numEntries_ ); - for ( unsigned int i = 0; i < numEntries_; ++i ) - vol[i] = getMeshEntryVolume( i ); - return vol; + static vector< double > vol; + vol.resize( numEntries_ ); + for ( unsigned int i = 0; i < numEntries_; ++i ) + vol[i] = getMeshEntryVolume( i ); + return vol; } const vector< double >& CylMesh::vGetVoxelMidpoint() const { - static vector< double > midpoint( numEntries_ * 3, 0.0 ); - midpoint.resize( numEntries_ * 3 ); - double dx = ( x1_ - x0_ ) / numEntries_; - double dy = ( y1_ - y0_ ) / numEntries_; - double dz = ( z1_ - z0_ ) / numEntries_; - vector< double >::iterator j = midpoint.begin(); - for ( unsigned int i = 0; i < numEntries_; ++i ) - *j++ = x0_ + dx * i; - for ( unsigned int i = 0; i < numEntries_; ++i ) - *j++ = y0_ + dy * i; - for ( unsigned int i = 0; i < numEntries_; ++i ) - *j++ = z0_ + dz * i; - - return midpoint; + static vector< double > midpoint( numEntries_ * 3, 0.0 ); + midpoint.resize( numEntries_ * 3 ); + double dx = ( x1_ - x0_ ) / numEntries_; + double dy = ( y1_ - y0_ ) / numEntries_; + double dz = ( z1_ - z0_ ) / numEntries_; + vector< double >::iterator j = midpoint.begin(); + for ( unsigned int i = 0; i < numEntries_; ++i ) + *j++ = x0_ + dx * i; + for ( unsigned int i = 0; i < numEntries_; ++i ) + *j++ = y0_ + dy * i; + for ( unsigned int i = 0; i < numEntries_; ++i ) + *j++ = z0_ + dz * i; + + return midpoint; } const vector< double >& CylMesh::getVoxelArea() const { - static vector< double > area; - area.resize( numEntries_ ); - for ( unsigned int i = 0; i < numEntries_; ++i ) { - double frac = ( 0.5 + static_cast< double >( i ) ) / - static_cast< double >( numEntries_ ); - double r = r0_ * ( 1.0 - frac ) + r1_ * frac; - area[i] = r * r * PI; - } - return area; + static vector< double > area; + area.resize( numEntries_ ); + for ( unsigned int i = 0; i < numEntries_; ++i ) { + double frac = ( 0.5 + static_cast< double >( i ) ) / + static_cast< double >( numEntries_ ); + double r = r0_ * ( 1.0 - frac ) + r1_ * frac; + area[i] = r * r * PI; + } + return area; } const vector< double >& CylMesh::getVoxelLength() const { - static vector< double > length; - length.assign( numEntries_, totLen_ / numEntries_ ); - return length; + static vector< double > length; + length.assign( numEntries_, totLen_ / numEntries_ ); + return length; } double CylMesh::vGetEntireVolume() const { - double vol = 0.0; - for ( unsigned int i = 0; i < numEntries_; ++i ) - vol += getMeshEntryVolume( i ); - return vol; + double vol = 0.0; + for ( unsigned int i = 0; i < numEntries_; ++i ) + vol += getMeshEntryVolume( i ); + return vol; } bool CylMesh::vSetVolumeNotRates( double volume ) { - double oldVol = vGetEntireVolume(); - double linScale = pow( volume/oldVol, 1.0 / 3.0 ); - x1_ *= linScale; - y1_ *= linScale; - z1_ *= linScale; - r0_ *= linScale; - r1_ *= linScale; - totLen_ *= linScale; - // Have to scale this so numEntries remains the same. - diffLength_ = totLen_ / numEntries_; - return true; + double oldVol = vGetEntireVolume(); + double linScale = pow( volume/oldVol, 1.0 / 3.0 ); + x1_ *= linScale; + y1_ *= linScale; + z1_ *= linScale; + r0_ *= linScale; + r1_ *= linScale; + totLen_ *= linScale; + // Have to scale this so numEntries remains the same. + diffLength_ = totLen_ / numEntries_; + return true; } ////////////////////////////////////////////////////////////////// @@ -740,43 +749,43 @@ bool CylMesh::vSetVolumeNotRates( double volume ) void CylMesh::transmitChange( const Eref& e ) { - /* - Id meshEntry( e.id().value() + 1 ); - assert( - meshEntry.eref().data() == reinterpret_cast< char* >( lookupEntry( 0 ) ) - ); - double oldvol = getMeshEntryVolume( 0 ); - unsigned int totalNumEntries = numEntries_; - unsigned int localNumEntries = totalNumEntries; - unsigned int startEntry = 0; - vector< unsigned int > localIndices( localNumEntries ); // empty - for ( unsigned int i = 0; i < localNumEntries; ++i ) - localIndices[i] = i; - vector< double > vols( localNumEntries, volume_ / numEntries_ ); - vector< vector< unsigned int > > outgoingEntries; // [node#][Entry#] - vector< vector< unsigned int > > incomingEntries; // [node#][Entry#] - - // This function updates the size of the FieldDataHandler for the - // MeshEntries. - DataHandler* dh = meshEntry.element()->dataHandler(); - FieldDataHandlerBase* fdh = dynamic_cast< FieldDataHandlerBase* >( dh ); - assert( fdh ); - if ( totalNumEntries > fdh->getMaxFieldEntries() ) { - fdh->setMaxFieldEntries( localNumEntries ); - } - - // This message tells the Stoich about the new mesh, and also about - // how it communicates with other nodes. - meshSplit()->fastSend( e, - oldvol, - vols, localIndices, - outgoingEntries, incomingEntries ); - - // This func goes down to the MeshEntry to tell all the pools and - // Reacs to deal with the new mesh. They then update the stoich. - lookupEntry( 0 )->triggerRemesh( meshEntry.eref(), - oldvol, startEntry, localIndices, vols ); - */ + /* + Id meshEntry( e.id().value() + 1 ); + assert( + meshEntry.eref().data() == reinterpret_cast< char* >( lookupEntry( 0 ) ) + ); + double oldvol = getMeshEntryVolume( 0 ); + unsigned int totalNumEntries = numEntries_; + unsigned int localNumEntries = totalNumEntries; + unsigned int startEntry = 0; + vector< unsigned int > localIndices( localNumEntries ); // empty + for ( unsigned int i = 0; i < localNumEntries; ++i ) + localIndices[i] = i; + vector< double > vols( localNumEntries, volume_ / numEntries_ ); + vector< vector< unsigned int > > outgoingEntries; // [node#][Entry#] + vector< vector< unsigned int > > incomingEntries; // [node#][Entry#] + + // This function updates the size of the FieldDataHandler for the + // MeshEntries. + DataHandler* dh = meshEntry.element()->dataHandler(); + FieldDataHandlerBase* fdh = dynamic_cast< FieldDataHandlerBase* >( dh ); + assert( fdh ); + if ( totalNumEntries > fdh->getMaxFieldEntries() ) { + fdh->setMaxFieldEntries( localNumEntries ); + } + + // This message tells the Stoich about the new mesh, and also about + // how it communicates with other nodes. + meshSplit()->fastSend( e, + oldvol, + vols, localIndices, + outgoingEntries, incomingEntries ); + + // This func goes down to the MeshEntry to tell all the pools and + // Reacs to deal with the new mesh. They then update the stoich. + lookupEntry( 0 )->triggerRemesh( meshEntry.eref(), + oldvol, startEntry, localIndices, vols ); + */ } ////////////////////////////////////////////////////////////////// @@ -784,40 +793,40 @@ void CylMesh::transmitChange( const Eref& e ) ////////////////////////////////////////////////////////////////// void CylMesh::buildStencil() { - setStencilSize( numEntries_, numEntries_ ); - for ( unsigned int i = 0; i < numEntries_; ++i ) { - double rLow = r0_ + i * rSlope_; - double rHigh = r0_ + (i + 1.0) * rSlope_; - double aLow = rLow * rLow * PI; - double aHigh = rHigh * rHigh * PI; - vector< double > entry; - vector< unsigned int > colIndex; - if ( i == 0 ) { - colIndex.push_back( 1 ); - entry.push_back( aHigh / diffLength_ ); - if ( isToroid_ ) { - colIndex.push_back( numEntries_ - 1 ); - entry.push_back( aLow / diffLength_ ); - } - } else if ( i == numEntries_ - 1 ) { - if ( isToroid_ ) { - colIndex.push_back( 0 ); - if ( r0_ < r1_ ) - entry.push_back( r0_ * r0_ * PI / diffLength_ ); - else - entry.push_back( r1_ * r1_ * PI / diffLength_ ); - } - colIndex.push_back( numEntries_ - 2 ); - entry.push_back( aLow / diffLength_ ); - } else { // Mostly it is in the middle. - colIndex.push_back( i - 1 ); - entry.push_back( aLow / diffLength_ ); - colIndex.push_back( i + 1 ); - entry.push_back( aHigh / diffLength_ ); - } - addRow( i, entry, colIndex ); - } - innerResetStencil(); + setStencilSize( numEntries_, numEntries_ ); + for ( unsigned int i = 0; i < numEntries_; ++i ) { + double rLow = r0_ + i * rSlope_; + double rHigh = r0_ + (i + 1.0) * rSlope_; + double aLow = rLow * rLow * PI; + double aHigh = rHigh * rHigh * PI; + vector< double > entry; + vector< unsigned int > colIndex; + if ( i == 0 ) { + colIndex.push_back( 1 ); + entry.push_back( aHigh / diffLength_ ); + if ( isToroid_ ) { + colIndex.push_back( numEntries_ - 1 ); + entry.push_back( aLow / diffLength_ ); + } + } else if ( i == numEntries_ - 1 ) { + if ( isToroid_ ) { + colIndex.push_back( 0 ); + if ( r0_ < r1_ ) + entry.push_back( r0_ * r0_ * PI / diffLength_ ); + else + entry.push_back( r1_ * r1_ * PI / diffLength_ ); + } + colIndex.push_back( numEntries_ - 2 ); + entry.push_back( aLow / diffLength_ ); + } else { // Mostly it is in the middle. + colIndex.push_back( i - 1 ); + entry.push_back( aLow / diffLength_ ); + colIndex.push_back( i + 1 ); + entry.push_back( aHigh / diffLength_ ); + } + addRow( i, entry, colIndex ); + } + innerResetStencil(); } ////////////////////////////////////////////////////////////////// @@ -825,15 +834,15 @@ void CylMesh::buildStencil() ////////////////////////////////////////////////////////////////// void CylMesh::matchMeshEntries( const ChemCompt* other, - vector< VoxelJunction >& ret ) const + vector< VoxelJunction >& ret ) const { - // This is seriously ugly, and what virtual funcs were meant to handle. - // Dirty hack for now. - const CylMesh* cyl = dynamic_cast< const CylMesh* >( other ); - if ( cyl ) { - matchCylMeshEntries( cyl, ret ); - return; - } + // This is seriously ugly, and what virtual funcs were meant to handle. + // Dirty hack for now. + const CylMesh* cyl = dynamic_cast< const CylMesh* >( other ); + if ( cyl ) { + matchCylMeshEntries( cyl, ret ); + return; + } const EndoMesh* em = dynamic_cast< const EndoMesh* >( other ); if ( em ) { @@ -841,18 +850,18 @@ void CylMesh::matchMeshEntries( const ChemCompt* other, flipRet( ret ); return; } - const CubeMesh* cube = dynamic_cast< const CubeMesh* >( other ); - if ( cube ) { - matchCubeMeshEntries( cube, ret ); - return; - } - const NeuroMesh* nm = dynamic_cast< const NeuroMesh* >( other ); - if ( nm ) { - matchNeuroMeshEntries( nm, ret ); - return; - } - cout << "Warning:CylMesh::matchMeshEntries: " << - " unknown mesh type\n"; + const CubeMesh* cube = dynamic_cast< const CubeMesh* >( other ); + if ( cube ) { + matchCubeMeshEntries( cube, ret ); + return; + } + const NeuroMesh* nm = dynamic_cast< const NeuroMesh* >( other ); + if ( nm ) { + matchNeuroMeshEntries( nm, ret ); + return; + } + cout << "Warning:CylMesh::matchMeshEntries: " << + " unknown mesh type\n"; } // Look for end-to-end diffusion, not sideways for now. @@ -861,161 +870,161 @@ void CylMesh::matchMeshEntries( const ChemCompt* other, void CylMesh::matchCylMeshEntries( const CylMesh* other, vector< VoxelJunction >& ret ) const { - const double EPSILON = 1e-3; - ret.clear(); - // Should really estimate the distance from the centre of the smaller - // cylinder cap disk to the plane of the larger disk, provided it is - // within the radius of the disk. The subsequent calculations are the - // same though. - double dr1 = // startOfSelf-to-startOfOther - distance(x0_ - other->x0_, y0_ - other->y0_, z0_ - other->z0_ ); - double dr2 = // endOfSelf-to-endOfOther - distance(x1_ - other->x1_, y1_ - other->y1_, z1_ - other->z1_ ); - double dr3 = // endOfSelf-to-startOfOther - distance(x1_ - other->x0_, y1_ - other->y0_, z1_ - other->z0_ ); - double dr4 = // startOfSelf-to-endOfOther - distance(x0_ - other->x1_, y0_ - other->y1_, z0_ - other->z1_ ); - - if ( dr1 <= dr2 && dr1 <= dr3 && dr1 <= dr4 ) { - if ( ( dr1/totLen_ < EPSILON && dr1/other->totLen_ < EPSILON ) ) { - double xda; - if ( r0_ < other->r0_ ) - xda = 2 * r0_ * r0_ * PI / ( diffLength_ + other->diffLength_ ); - else - xda = 2 * other->r0_ * other->r0_ * PI / - ( diffLength_ + other->diffLength_ ); - ret.push_back( VoxelJunction( 0, 0, xda ) ); - ret.back().first = 0; - ret.back().second = 0; - ret.back().firstVol = getMeshEntryVolume( 0 ); - ret.back().secondVol = other->getMeshEntryVolume( 0 ); - } - } else if ( dr2 <= dr3 && dr2 <= dr4 ) { - if ( ( dr2/totLen_ < EPSILON && dr2/other->totLen_ < EPSILON ) ) { - double xda; - if ( r1_ < other->r1_ ) - xda = 2 * r1_ * r1_ * PI / ( diffLength_ + other->diffLength_ ); - else - xda = 2 * other->r1_ * other->r1_ * PI / - ( diffLength_ + other->diffLength_ ); - ret.push_back( VoxelJunction( numEntries_ - 1, - other->numEntries_ - 1, xda ) ); - ret.back().first = numEntries_; - ret.back().second = other->numEntries_ - 1; - ret.back().firstVol = getMeshEntryVolume( numEntries_ - 1 ); - ret.back().secondVol = other->getMeshEntryVolume( other->numEntries_ - 1 ); - } - } else if ( dr3 <= dr4 ) { - if ( ( dr3/totLen_ < EPSILON && dr3/other->totLen_ < EPSILON ) ) { - double xda; - if ( r1_ < other->r0_ ) - xda = 2 * r1_ * r1_ * PI / ( diffLength_ + other->diffLength_ ); - else - xda = 2 * other->r0_ * other->r0_ * PI / - ( diffLength_ + other->diffLength_ ); - ret.push_back( VoxelJunction( numEntries_ - 1, 0, xda ) ); - ret.back().first = numEntries_ - 1; - ret.back().second = 0; - ret.back().firstVol = getMeshEntryVolume( numEntries_ - 1 ); - ret.back().secondVol = other->getMeshEntryVolume( 0 ); - } - } else { - if ( ( dr4/totLen_ < EPSILON && dr4/other->totLen_ < EPSILON ) ) { - double xda; - if ( r0_ < other->r1_ ) - xda = 2 * r0_ * r0_ * PI / ( diffLength_ + other->diffLength_ ); - else - xda = 2 * other->r1_ * other->r1_ * PI / - ( diffLength_ + other->diffLength_ ); - ret.push_back( VoxelJunction( 0, other->numEntries_ - 1, xda )); - ret.back().first = 0; - ret.back().second = other->numEntries_ - 1; - ret.back().firstVol = getMeshEntryVolume( 0 ); - ret.back().secondVol = other->getMeshEntryVolume( other->numEntries_ - 1 ); - } - } + const double EPSILON = 1e-3; + ret.clear(); + // Should really estimate the distance from the centre of the smaller + // cylinder cap disk to the plane of the larger disk, provided it is + // within the radius of the disk. The subsequent calculations are the + // same though. + double dr1 = // startOfSelf-to-startOfOther + distance(x0_ - other->x0_, y0_ - other->y0_, z0_ - other->z0_ ); + double dr2 = // endOfSelf-to-endOfOther + distance(x1_ - other->x1_, y1_ - other->y1_, z1_ - other->z1_ ); + double dr3 = // endOfSelf-to-startOfOther + distance(x1_ - other->x0_, y1_ - other->y0_, z1_ - other->z0_ ); + double dr4 = // startOfSelf-to-endOfOther + distance(x0_ - other->x1_, y0_ - other->y1_, z0_ - other->z1_ ); + + if ( dr1 <= dr2 && dr1 <= dr3 && dr1 <= dr4 ) { + if ( ( dr1/totLen_ < EPSILON && dr1/other->totLen_ < EPSILON ) ) { + double xda; + if ( r0_ < other->r0_ ) + xda = 2 * r0_ * r0_ * PI / ( diffLength_ + other->diffLength_ ); + else + xda = 2 * other->r0_ * other->r0_ * PI / + ( diffLength_ + other->diffLength_ ); + ret.push_back( VoxelJunction( 0, 0, xda ) ); + ret.back().first = 0; + ret.back().second = 0; + ret.back().firstVol = getMeshEntryVolume( 0 ); + ret.back().secondVol = other->getMeshEntryVolume( 0 ); + } + } else if ( dr2 <= dr3 && dr2 <= dr4 ) { + if ( ( dr2/totLen_ < EPSILON && dr2/other->totLen_ < EPSILON ) ) { + double xda; + if ( r1_ < other->r1_ ) + xda = 2 * r1_ * r1_ * PI / ( diffLength_ + other->diffLength_ ); + else + xda = 2 * other->r1_ * other->r1_ * PI / + ( diffLength_ + other->diffLength_ ); + ret.push_back( VoxelJunction( numEntries_ - 1, + other->numEntries_ - 1, xda ) ); + ret.back().first = numEntries_; + ret.back().second = other->numEntries_ - 1; + ret.back().firstVol = getMeshEntryVolume( numEntries_ - 1 ); + ret.back().secondVol = other->getMeshEntryVolume( other->numEntries_ - 1 ); + } + } else if ( dr3 <= dr4 ) { + if ( ( dr3/totLen_ < EPSILON && dr3/other->totLen_ < EPSILON ) ) { + double xda; + if ( r1_ < other->r0_ ) + xda = 2 * r1_ * r1_ * PI / ( diffLength_ + other->diffLength_ ); + else + xda = 2 * other->r0_ * other->r0_ * PI / + ( diffLength_ + other->diffLength_ ); + ret.push_back( VoxelJunction( numEntries_ - 1, 0, xda ) ); + ret.back().first = numEntries_ - 1; + ret.back().second = 0; + ret.back().firstVol = getMeshEntryVolume( numEntries_ - 1 ); + ret.back().secondVol = other->getMeshEntryVolume( 0 ); + } + } else { + if ( ( dr4/totLen_ < EPSILON && dr4/other->totLen_ < EPSILON ) ) { + double xda; + if ( r0_ < other->r1_ ) + xda = 2 * r0_ * r0_ * PI / ( diffLength_ + other->diffLength_ ); + else + xda = 2 * other->r1_ * other->r1_ * PI / + ( diffLength_ + other->diffLength_ ); + ret.push_back( VoxelJunction( 0, other->numEntries_ - 1, xda )); + ret.back().first = 0; + ret.back().second = other->numEntries_ - 1; + ret.back().firstVol = getMeshEntryVolume( 0 ); + ret.back().secondVol = other->getMeshEntryVolume( other->numEntries_ - 1 ); + } + } } // Select grid volume. Ideally the meshes should be comparable. double CylMesh::selectGridVolume( double h ) const { - if ( h > diffLength_ ) - h = diffLength_; - if ( h > r0_ ) - h = r0_; - if ( h > r1_ ) - h = r1_; - h *= surfaceGranularity_; - unsigned int num = ceil( diffLength_ / h ); - h = diffLength_ / num; - - return h; + if ( h > diffLength_ ) + h = diffLength_; + if ( h > r0_ ) + h = r0_; + if ( h > r1_ ) + h = r1_; + h *= surfaceGranularity_; + unsigned int num = ceil( diffLength_ / h ); + h = diffLength_ / num; + + return h; } void fillPointsOnCircle( - const Vec& u, const Vec& v, const Vec& q, - double h, double r, vector< double >& area, - const CubeMesh* other - ) + const Vec& u, const Vec& v, const Vec& q, + double h, double r, vector< double >& area, + const CubeMesh* other + ) { - // fine-tune the h spacing so it is integral around circle. - // This will cause small errors in area estimate but they will - // be anisotropic. The alternative will have large errors toward - // 360 degrees, but not elsewhere. - unsigned int numAngle = floor( 2.0 * PI * r / h + 0.5 ); - assert( numAngle > 0 ); - double dtheta = 2.0 * PI / numAngle; - double dArea = h * dtheta * r; - // March along points on surface of circle centred at q. - for ( unsigned int j = 0; j < numAngle; ++j ) { - double theta = j * dtheta; - double c = cos( theta ); - double s = sin( theta ); - double p0 = q.a0() + r * ( u.a0() * c + v.a0() * s ); - double p1 = q.a1() + r * ( u.a1() * c + v.a1() * s ); - double p2 = q.a2() + r * ( u.a2() * c + v.a2() * s ); - unsigned int index = other->spaceToIndex( p0, p1, p2 ); - if ( index != CubeMesh::EMPTY ) - area[index] += dArea; - } + // fine-tune the h spacing so it is integral around circle. + // This will cause small errors in area estimate but they will + // be anisotropic. The alternative will have large errors toward + // 360 degrees, but not elsewhere. + unsigned int numAngle = floor( 2.0 * PI * r / h + 0.5 ); + assert( numAngle > 0 ); + double dtheta = 2.0 * PI / numAngle; + double dArea = h * dtheta * r; + // March along points on surface of circle centred at q. + for ( unsigned int j = 0; j < numAngle; ++j ) { + double theta = j * dtheta; + double c = cos( theta ); + double s = sin( theta ); + double p0 = q.a0() + r * ( u.a0() * c + v.a0() * s ); + double p1 = q.a1() + r * ( u.a1() * c + v.a1() * s ); + double p2 = q.a2() + r * ( u.a2() * c + v.a2() * s ); + unsigned int index = other->spaceToIndex( p0, p1, p2 ); + if ( index != CubeMesh::EMPTY ) + area[index] += dArea; + } } void CylMesh::matchCubeMeshEntries( const CubeMesh* other, vector< VoxelJunction >& ret ) const { - const double EPSILON = 1e-18; - Vec a( x1_ - x0_, y1_ - y0_, z1_ - z0_ ); - Vec u; - Vec v; - a.orthogonalAxes( u, v ); - - double h = selectGridVolume( other->getDx() ); - - unsigned int num = floor( 0.1 + diffLength_ / h ); - // March along axis of cylinder. - // q is the location of the point along axis. - for ( unsigned int i = 0; i < numEntries_; ++i ) { - vector< double >area( other->getNumEntries(), 0.0 ); - for ( unsigned int j = 0; j < num; ++j ) { - unsigned int m = i * num + j; - double frac = ( m * h + h/2.0 ) / totLen_; - double q0 = x0_ + a.a0() * frac; - double q1 = y0_ + a.a1() * frac; - double q2 = z0_ + a.a2() * frac; - // get radius of cylinder at this point. - double r = r0_ + ( m * h + h / 2.0 ) * rSlope_; - fillPointsOnCircle( u, v, Vec( q0, q1, q2 ), - h, r, area, other ); - } - // Go through all cubeMesh entries and compute diffusion - // cross-section. Assume this is through a membrane, so the - // only factor relevant is area. Not the distance. - for ( unsigned int k = 0; k < area.size(); ++k ) { - if ( area[k] > EPSILON ) { - ret.push_back( VoxelJunction( i, k, area[k] ) ); - } - } - } + const double EPSILON = 1e-18; + Vec a( x1_ - x0_, y1_ - y0_, z1_ - z0_ ); + Vec u; + Vec v; + a.orthogonalAxes( u, v ); + + double h = selectGridVolume( other->getDx() ); + + unsigned int num = floor( 0.1 + diffLength_ / h ); + // March along axis of cylinder. + // q is the location of the point along axis. + for ( unsigned int i = 0; i < numEntries_; ++i ) { + vector< double >area( other->getNumEntries(), 0.0 ); + for ( unsigned int j = 0; j < num; ++j ) { + unsigned int m = i * num + j; + double frac = ( m * h + h/2.0 ) / totLen_; + double q0 = x0_ + a.a0() * frac; + double q1 = y0_ + a.a1() * frac; + double q2 = z0_ + a.a2() * frac; + // get radius of cylinder at this point. + double r = r0_ + ( m * h + h / 2.0 ) * rSlope_; + fillPointsOnCircle( u, v, Vec( q0, q1, q2 ), + h, r, area, other ); + } + // Go through all cubeMesh entries and compute diffusion + // cross-section. Assume this is through a membrane, so the + // only factor relevant is area. Not the distance. + for ( unsigned int k = 0; k < area.size(); ++k ) { + if ( area[k] > EPSILON ) { + ret.push_back( VoxelJunction( i, k, area[k] ) ); + } + } + } } void CylMesh::matchNeuroMeshEntries( const NeuroMesh* other, @@ -1024,111 +1033,111 @@ vector< VoxelJunction >& ret ) const } void CylMesh::indexToSpace( unsigned int index, - double& x, double& y, double& z ) const + double& x, double& y, double& z ) const { - if ( index < numEntries_ ) { - double k = ( index + 0.5 ) / static_cast< double >( numEntries_ ); - x = ( x1_ - x0_ ) * k + x0_; - y = ( y1_ - y0_ ) * k + y0_; - z = ( z1_ - z0_ ) * k + z0_; - } + if ( index < numEntries_ ) { + double k = ( index + 0.5 ) / static_cast< double >( numEntries_ ); + x = ( x1_ - x0_ ) * k + x0_; + y = ( y1_ - y0_ ) * k + y0_; + z = ( z1_ - z0_ ) * k + z0_; + } } static double dotprd ( double x0, double y0, double z0, - double x1, double y1, double z1 ) + double x1, double y1, double z1 ) { - return x0 * x1 + y0 * y1 + z0 * z1; + return x0 * x1 + y0 * y1 + z0 * z1; } // this is the function that does the actual calculation. double CylMesh::nearest( double x, double y, double z, - double& linePos, double& r ) const + double& linePos, double& r ) const { - // Consider r0 = x0,y0,z0 and r1 = x1, y1, z1, and r = x,y,z. - // Fraction along cylinder = k - // - // Then, point p along line from r0 to r1 is - // p = k( r0-r1) + r1. - // - // Solving, - // k = (r0 - r1).(r - r1) / (|r0-r1|^2) - // - - double dist = distance( x1_ - x0_, y1_ - y0_, z1_ - z0_ ); - double k = dotprd( - x1_ - x0_, y1_ - y0_, z1_ - z0_, - x - x0_, y - y0_, z - z0_ ) / ( dist * dist ); - - // x2, y2, z2 are the coords of the nearest point. - double x2 = k * (x1_ - x0_) + x0_; - double y2 = k * (y1_ - y0_) + y0_; - double z2 = k * (z1_ - z0_) + z0_; - - double ret = distance( x - x2, y - y2, z - z2 ); - linePos = k; - r = r0_ + k * numEntries_ * rSlope_; - return ret; + // Consider r0 = x0,y0,z0 and r1 = x1, y1, z1, and r = x,y,z. + // Fraction along cylinder = k + // + // Then, point p along line from r0 to r1 is + // p = k( r0-r1) + r1. + // + // Solving, + // k = (r0 - r1).(r - r1) / (|r0-r1|^2) + // + + double dist = distance( x1_ - x0_, y1_ - y0_, z1_ - z0_ ); + double k = dotprd( + x1_ - x0_, y1_ - y0_, z1_ - z0_, + x - x0_, y - y0_, z - z0_ ) / ( dist * dist ); + + // x2, y2, z2 are the coords of the nearest point. + double x2 = k * (x1_ - x0_) + x0_; + double y2 = k * (y1_ - y0_) + y0_; + double z2 = k * (z1_ - z0_) + z0_; + + double ret = distance( x - x2, y - y2, z - z2 ); + linePos = k; + r = r0_ + k * numEntries_ * rSlope_; + return ret; } // This function returns the index. double CylMesh::nearest( double x, double y, double z, - unsigned int& index ) const + unsigned int& index ) const { - double k = 0.0; - double r; - double ret = nearest( x, y, z, k, r ); - if ( k < 0.0 ) { - ret = -ret; - index = 0; - } else if ( k > 1.0 ) { - ret = -ret; - index = numEntries_ - 1; - } else { // Inside length of cylinder, now is it inside radius? - index = k * numEntries_; - double ri = r0_ + (index + 0.5) * rSlope_; - if ( ret > ri ) - ret = -ret; - } - return ret; + double k = 0.0; + double r; + double ret = nearest( x, y, z, k, r ); + if ( k < 0.0 ) { + ret = -ret; + index = 0; + } else if ( k > 1.0 ) { + ret = -ret; + index = numEntries_ - 1; + } else { // Inside length of cylinder, now is it inside radius? + index = k * numEntries_; + double ri = r0_ + (index + 0.5) * rSlope_; + if ( ret > ri ) + ret = -ret; + } + return ret; } /* bool isOnSurface( double x, double y, double z, - double dx, double dy, double dz, - unsigned int &index, double& adx ) + double dx, double dy, double dz, + unsigned int &index, double& adx ) { - double len = distance( x1_ - x0_, y1_ - y0_, z1_ - z0_ ); - double k = dotprd( - x1_ - x0_, y1_ - y0_, z1_ - z0_, - x - x0_, y - y0_, z - z0_ ) / len; + double len = distance( x1_ - x0_, y1_ - y0_, z1_ - z0_ ); + double k = dotprd( + x1_ - x0_, y1_ - y0_, z1_ - z0_, + x - x0_, y - y0_, z - z0_ ) / len; - // x2, y2, z2 are the coords of the nearest point. - double x2 = k * (x1_ - x0_) + x0_; - double y2 = k * (y1_ - y0_) + y0_; - double z2 = k * (z1_ - z0_) + z0_; + // x2, y2, z2 are the coords of the nearest point. + double x2 = k * (x1_ - x0_) + x0_; + double y2 = k * (y1_ - y0_) + y0_; + double z2 = k * (z1_ - z0_) + z0_; - double ret = distance( x - x2, y - y2, z - z2 ); + double ret = distance( x - x2, y - y2, z - z2 ); - double cubeRange = sqrt(dx*dx + dy*dy + dz*dz); + double cubeRange = sqrt(dx*dx + dy*dy + dz*dz); - // Now we check if the distance is definitely too far off for the - // passed in point - if ( k < -dx/2 || k > len + dx/2 ) // past the end. - return false; + // Now we check if the distance is definitely too far off for the + // passed in point + if ( k < -dx/2 || k > len + dx/2 ) // past the end. + return false; - double ri = k * rSlope_; // local cylinder radius. + double ri = k * rSlope_; // local cylinder radius. - if ( ret > ri + cubeRange || ret < ri - cubeRange ) - return false; + if ( ret > ri + cubeRange || ret < ri - cubeRange ) + return false; - // OK, now we need to find the plane of intersection of the cylinder - // with the cuboid. To make it easier, assume it is flat. We already - // know the vector from the middle of the cuboid to the nearest - // cylinder point. Treat it as the normal to the intersection plane. - // We need: : is the plane inside the cube? - // What is the area of the plane till its intersection with the cube? + // OK, now we need to find the plane of intersection of the cylinder + // with the cuboid. To make it easier, assume it is flat. We already + // know the vector from the middle of the cuboid to the nearest + // cylinder point. Treat it as the normal to the intersection plane. + // We need: : is the plane inside the cube? + // What is the area of the plane till its intersection with the cube? } diff --git a/pymoose/PyRun.cpp b/pymoose/PyRun.cpp index 5b3db0da..53e33c6b 100644 --- a/pymoose/PyRun.cpp +++ b/pymoose/PyRun.cpp @@ -3,47 +3,7 @@ // Filename: PyRun.cpp // Description: // Author: subha -// Maintainer: // Created: Sat Oct 11 14:47:22 2014 (+0530) -// Version: -// Last-Updated: Fri Jun 19 18:56:06 2015 (-0400) -// By: Subhasis Ray -// Update #: 15 -// URL: -// Keywords: -// Compatibility: -// -// - -// Commentary: -// -// -// -// - -// Change log: -// -// -// -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 3, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; see the file COPYING. If not, write to -// the Free Software Foundation, Inc., 51 Franklin Street, Fifth -// Floor, Boston, MA 02110-1301, USA. -// -// - -// Code: #include "Python.h" #include "../basecode/header.h" @@ -147,7 +107,8 @@ const Cinfo * PyRun::initCinfo() "for the Reinit operation. It also uses ProcInfo. ", processShared, sizeof( processShared ) / sizeof( Finfo* )); - static Finfo * pyRunFinfos[] = { + static Finfo * pyRunFinfos[] = + { &runstring, &initstring, &mode, @@ -161,10 +122,12 @@ const Cinfo * PyRun::initCinfo() &proc, }; - static string doc[] = { + static string doc[] = + { "Name", "PyRun", "Author", "Subhasis Ray", - "Description", "Runs Python statements from inside MOOSE."}; + "Description", "Runs Python statements from inside MOOSE." + }; static Dinfo< PyRun > dinfo; static Cinfo pyRunCinfo( "PyRun", @@ -180,21 +143,24 @@ const Cinfo * PyRun::initCinfo() static const Cinfo * pyRunCinfo = PyRun::initCinfo(); PyRun::PyRun():mode_(0), initstr_(""), runstr_(""), - globals_(0), locals_(0), - runcompiled_(0), initcompiled_(0), - inputvar_("input_"), outputvar_("output") + globals_(0), locals_(0), + runcompiled_(0), initcompiled_(0), + inputvar_("input_"), outputvar_("output") { locals_ = PyDict_New(); - if (!locals_){ + if (!locals_) + { cerr << "Could not initialize locals dict" << endl; return; } PyObject * value = PyFloat_FromDouble(0.0); - if (!value && PyErr_Occurred()){ + if (!value && PyErr_Occurred()) + { PyErr_Print(); return; } - if (PyDict_SetItemString(locals_, inputvar_.c_str(), value)){ + if (PyDict_SetItemString(locals_, inputvar_.c_str(), value)) + { PyErr_Print(); } } @@ -259,34 +225,44 @@ int PyRun::getMode() const void PyRun::trigger(const Eref& e, double input) { - if (!runcompiled_){ + if (!runcompiled_) + { return; } - if (mode_ == 1){ + if (mode_ == 1) + { return; } PyObject * value = PyDict_GetItemString(locals_, inputvar_.c_str()); - if (value){ + if (value) + { Py_DECREF(value); } value = PyFloat_FromDouble(input); - if (!value && PyErr_Occurred()){ + if (!value && PyErr_Occurred()) + { PyErr_Print(); } - if (PyDict_SetItemString(locals_, inputvar_.c_str(), value)){ + if (PyDict_SetItemString(locals_, inputvar_.c_str(), value)) + { PyErr_Print(); } PyEval_EvalCode(runcompiled_, globals_, locals_); - if (PyErr_Occurred()){ + if (PyErr_Occurred()) + { PyErr_Print (); } value = PyDict_GetItemString(locals_, outputvar_.c_str()); - if (value){ + if (value) + { double output = PyFloat_AsDouble(value); - if (PyErr_Occurred()){ + if (PyErr_Occurred()) + { PyErr_Print (); - } else { + } + else + { outputOut()->send(e, output); } } @@ -296,36 +272,49 @@ void PyRun::run(const Eref&e, string statement) { PyRun_SimpleString(statement.c_str()); PyObject * value = PyDict_GetItemString(locals_, outputvar_.c_str()); - if (value){ + if (value) + { double output = PyFloat_AsDouble(value); - if (PyErr_Occurred()){ + if (PyErr_Occurred()) PyErr_Print (); - } else { + else outputOut()->send(e, output); - } } } void PyRun::process(const Eref & e, ProcPtr p) { + // Make sure the get the GIL. Ksolve/Gsolve can be multithreaded. + PyGILState_STATE gstate = PyGILState_Ensure(); + // PyRun_String(runstr_.c_str(), 0, globals_, locals_); // PyRun_SimpleString(runstr_.c_str()); - if (!runcompiled_ || mode_ == 2){ + if (! runcompiled_ || mode_ == 2) + { return; } + PyEval_EvalCode(runcompiled_, globals_, locals_); - if (PyErr_Occurred()){ + if (PyErr_Occurred()) + { PyErr_Print (); + return; } + PyObject * value = PyDict_GetItemString(locals_, outputvar_.c_str()); - if (value){ + if (value) + { double output = PyFloat_AsDouble(value); - if (PyErr_Occurred()){ + if (PyErr_Occurred()) + { PyErr_Print (); - } else { - outputOut()->send(e, output); + return; } + else + outputOut()->send(e, output); } + + PyGILState_Release( gstate ); } /** @@ -337,19 +326,25 @@ void handleError(bool syntax) PyObject *exc, *val, *trb; char * msg; - if (syntax && PyErr_ExceptionMatches (PyExc_SyntaxError)){ + if (syntax && PyErr_ExceptionMatches (PyExc_SyntaxError)) + { PyErr_Fetch (&exc, &val, &trb); /* clears exception! */ if (PyArg_ParseTuple (val, "sO", &msg, &trb) && - !strcmp (msg, "unexpected EOF while parsing")){ /* E_EOF */ + !strcmp (msg, "unexpected EOF while parsing")) /* E_EOF */ + { Py_XDECREF (exc); Py_XDECREF (val); Py_XDECREF (trb); - } else { /* some other syntax error */ + } + else /* some other syntax error */ + { PyErr_Restore (exc, val, trb); PyErr_Print (); } - } else { /* some non-syntax error */ + } + else /* some non-syntax error */ + { PyErr_Print (); } } @@ -357,45 +352,55 @@ void handleError(bool syntax) void PyRun::reinit(const Eref& e, ProcPtr p) { PyObject * main_module; - if (globals_ == NULL){ + if (globals_ == NULL) + { main_module = PyImport_AddModule("__main__"); globals_ = PyModule_GetDict(main_module); Py_XINCREF(globals_); } - if (locals_ == NULL){ + if (locals_ == NULL) + { locals_ = PyDict_New(); - if (!locals_){ + if (!locals_) + { cerr << "Could not initialize locals dict" << endl; } } initcompiled_ = (PYCODEOBJECT*)Py_CompileString( - initstr_.c_str(), - get_program_name().c_str(), - Py_file_input); - if (!initcompiled_){ + initstr_.c_str(), + get_program_name().c_str(), + Py_file_input); + if (!initcompiled_) + { cerr << "Error compiling initString" << endl; handleError(true); - } else { + } + else + { PyEval_EvalCode(initcompiled_, globals_, locals_); - if (PyErr_Occurred()){ + if (PyErr_Occurred()) + { PyErr_Print (); } } + + assert( runstr_.size() > 0 ); + runcompiled_ = (PYCODEOBJECT*)Py_CompileString( - runstr_.c_str(), - get_program_name().c_str(), - Py_file_input); - if (!runcompiled_){ + runstr_.c_str(), + get_program_name().c_str(), + Py_file_input); + if (!runcompiled_) + { cerr << "Error compiling runString" << endl; handleError(true); - } else { + } + else + { PyEval_EvalCode(runcompiled_, globals_, locals_); - if (PyErr_Occurred()){ + if (PyErr_Occurred()) + { PyErr_Print (); } } } - - -// -// PyRun.cpp ends here diff --git a/pymoose/PyRun.h b/pymoose/PyRun.h index 8f8d0612..ab0d1bdd 100644 --- a/pymoose/PyRun.h +++ b/pymoose/PyRun.h @@ -1,49 +1,7 @@ -// PyRun.h --- -// // Filename: PyRun.h // Description: // Author: subha -// Maintainer: // Created: Sat Oct 11 14:40:45 2014 (+0530) -// Version: -// Last-Updated: Fri Jun 19 18:54:49 2015 (-0400) -// By: Subhasis Ray -// Update #: 31 -// URL: -// Keywords: -// Compatibility: -// -// - -// Commentary: -// -// Class to call Python functions from MOOSE -// -// - -// Change log: -// -// -// -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 3, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; see the file COPYING. If not, write to -// the Free Software Foundation, Inc., 51 Franklin Street, Fifth -// Floor, Boston, MA 02110-1301, USA. -// -// - -// Code: #ifndef _PYCALL_H #define _PYCALL_H @@ -55,19 +13,19 @@ string get_program_name() { - wchar_t * progname = Py_GetProgramName(); - char buffer[PATH_MAX+1]; - size_t ret = wcstombs(buffer, progname, PATH_MAX); - buffer[ret] = '\0'; - return string(buffer); + wchar_t * progname = Py_GetProgramName(); + char buffer[PATH_MAX+1]; + size_t ret = wcstombs(buffer, progname, PATH_MAX); + buffer[ret] = '\0'; + return string(buffer); } #else #define PYCODEOBJECT PyCodeObject string get_program_name() { - char * progname = Py_GetProgramName(); - return string(progname); + char * progname = Py_GetProgramName(); + return string(progname); } #endif @@ -107,7 +65,8 @@ public: void run(const Eref& e, string statement); - void trigger(const Eref& e, double input); // this is a way to trigger execution via incoming message - can be useful for debugging + // this is a way to trigger execution via incoming message - can be useful for debugging + void trigger(const Eref& e, double input); void process(const Eref& e, ProcPtr p); void reinit(const Eref& e, ProcPtr p); @@ -115,20 +74,15 @@ public: static const Cinfo * initCinfo(); protected: - int mode_; // flag to decide when to run the Python string - string initstr_; // statement str for running at reinit - string runstr_; // statement str for running in each process call - PyObject * globals_; // global env dict - PyObject * locals_; // local env dict - PYCODEOBJECT * runcompiled_; // compiled form of procstr_ + int mode_; // flag to decide when to run the Python string + string initstr_; // statement str for running at reinit + string runstr_; // statement str for running in each process call + PyObject * globals_; // global env dict + PyObject * locals_; // local env dict + PYCODEOBJECT * runcompiled_; // compiled form of procstr_ PYCODEOBJECT * initcompiled_; // coimpiled form of initstr_ - string inputvar_; // identifier for input variable. - string outputvar_; // identifier for output variable + string inputvar_; // identifier for input variable. + string outputvar_; // identifier for output variable }; #endif - - - -// -// PyRun.h ends here diff --git a/pymoose/moosemodule.cpp b/pymoose/moosemodule.cpp index acf86f02..fe702125 100644 --- a/pymoose/moosemodule.cpp +++ b/pymoose/moosemodule.cpp @@ -164,8 +164,8 @@ int verbosity = 1; // static int isSingleThreaded = 0; static int isInfinite = 0; static unsigned int numNodes = 1; -static unsigned int numCores = 1; -static unsigned int myNode = 0; +// static unsigned int numCores = 1; +// static unsigned int myNode = 0; // static unsigned int numProcessThreads = 0; static int doUnitTests = 0; static int doRegressionTests = 0; @@ -899,6 +899,7 @@ vector <string> setup_runtime_env() args.push_back("-i"); } } +#if 0 it = argmap.find("NUMNODES"); if (it != argmap.end()) { @@ -917,6 +918,7 @@ vector <string> setup_runtime_env() // args.push_back("-t"); // args.push_back(it->second); // } +#endif it = argmap.find("QUIT"); if (it != argmap.end()) { @@ -1720,14 +1722,6 @@ PyObject * moose_start(PyObject * dummy, PyObject * args ) sigHandler.sa_flags = 0; sigaction(SIGINT, &sigHandler, NULL); -#if 0 - // NOTE: (dilawar) Does not know if Py_BEGIN_ALLOW_THREADS is - // neccessary. - // Py_BEGIN_ALLOW_THREADS - SHELLPTR->doStart(runtime); - // Py_END_ALLOW_THREADS - Py_RETURN_NONE; -#endif SHELLPTR->doStart( runtime, notify ); Py_RETURN_NONE; @@ -3199,10 +3193,10 @@ PyMODINIT_FUNC MODINIT(_moose) PyModule_AddObject(moose_module, "DestField", (PyObject*)&moose_DestField); // PyModule_AddIntConstant(moose_module, "SINGLETHREADED", isSingleThreaded); - PyModule_AddIntConstant(moose_module, "NUMCORES", numCores); - PyModule_AddIntConstant(moose_module, "NUMNODES", numNodes); + // PyModule_AddIntConstant(moose_module, "NUMCORES", numCores); + // PyModule_AddIntConstant(moose_module, "NUMNODES", numNodes); // PyModule_AddIntConstant(moose_module, "NUMPTHREADS", numProcessThreads); - PyModule_AddIntConstant(moose_module, "MYNODE", myNode); + // PyModule_AddIntConstant(moose_module, "MYNODE", myNode); PyModule_AddIntConstant(moose_module, "INFINITE", isInfinite); PyModule_AddStringConstant(moose_module, "__version__", SHELLPTR->doVersion().c_str()); PyModule_AddStringConstant(moose_module, "VERSION", SHELLPTR->doVersion().c_str()); @@ -3233,13 +3227,11 @@ PyMODINIT_FUNC MODINIT(_moose) ); if (doUnitTests) - { test_moosemodule(); - } #ifdef PY3K return moose_module; #endif -} //! init_moose +} ////////////////////////////////////////////// diff --git a/pymoose/pymooseinit.cpp b/pymoose/pymooseinit.cpp index e5719af6..29200a67 100644 --- a/pymoose/pymooseinit.cpp +++ b/pymoose/pymooseinit.cpp @@ -9,9 +9,9 @@ #include "header.h" #ifndef WIN32 - #include <sys/time.h> +#include <sys/time.h> #else - #include <time.h> +#include <time.h> #endif #include <math.h> #include <queue> @@ -66,10 +66,10 @@ extern void destroyMsgManagers(); // void regressionTests(); #endif extern void speedTestMultiNodeIntFireNetwork( - unsigned int size, unsigned int runsteps ); + unsigned int size, unsigned int runsteps ); #ifdef USE_SMOLDYN - extern void testSmoldyn(); +extern void testSmoldyn(); #endif // bool benchmarkTests( int argc, char** argv ); @@ -81,44 +81,44 @@ extern void mooseBenchmarks( unsigned int option ); unsigned int getNumCores() { - unsigned int numCPU = 0; + unsigned int numCPU = 0; #ifdef WIN_32 - SYSTEM_INFO sysinfo; - GetSystemInfo( &sysinfo ); + SYSTEM_INFO sysinfo; + GetSystemInfo( &sysinfo ); - numCPU = sysinfo.dwNumberOfProcessors; + numCPU = sysinfo.dwNumberOfProcessors; #endif #ifdef LINUX - numCPU = sysconf( _SC_NPROCESSORS_ONLN ); + numCPU = sysconf( _SC_NPROCESSORS_ONLN ); #endif #ifdef MACOSX - int mib[4]; - size_t len = sizeof(numCPU); + int mib[4]; + size_t len = sizeof(numCPU); - /* set the mib for hw.ncpu */ - mib[0] = CTL_HW; - mib[1] = HW_AVAILCPU; // alternatively, try HW_NCPU; + /* set the mib for hw.ncpu */ + mib[0] = CTL_HW; + mib[1] = HW_AVAILCPU; // alternatively, try HW_NCPU; - /* get the number of CPUs from the system */ - sysctl(mib, 2, &numCPU, &len, NULL, 0); + /* get the number of CPUs from the system */ + sysctl(mib, 2, &numCPU, &len, NULL, 0); - if( numCPU < 1 ) - { - mib[1] = HW_NCPU; - sysctl( mib, 2, &numCPU, &len, NULL, 0 ); - } + if( numCPU < 1 ) + { + mib[1] = HW_NCPU; + sysctl( mib, 2, &numCPU, &len, NULL, 0 ); + } #endif - if ( numCPU < 1 ) - { -#ifndef QUIET_MODE - cout << "No CPU information available. Assuming single core." << endl; -#else + +#if 0 + if ( numCPU < 1 ) + { + cout << "No CPU information available. Assuming single core." << endl; + numCPU = 1; + } #endif - numCPU = 1; - } - return numCPU; + return numCPU; } bool quitFlag = 0; @@ -127,138 +127,141 @@ bool quitFlag = 0; void checkChildren( Id parent, const string& info ) { - vector< Id > ret; - Neutral::children( parent.eref(), ret ); - cout << info << " checkChildren of " << - parent.element()->getName() << ": " << - ret.size() << " children\n"; - for ( vector< Id >::iterator i = ret.begin(); i != ret.end(); ++i ) - { - cout << i->element()->getName() << endl; - } + vector< Id > ret; + Neutral::children( parent.eref(), ret ); + cout << info << " checkChildren of " << + parent.element()->getName() << ": " << + ret.size() << " children\n"; + for ( vector< Id >::iterator i = ret.begin(); i != ret.end(); ++i ) + { + cout << i->element()->getName() << endl; + } } Id init( int argc, char** argv, bool& doUnitTests, bool& doRegressionTests, - unsigned int& benchmark ) + unsigned int& benchmark ) { - unsigned int numCores = getNumCores(); - int numNodes = 1; - int myNode = 0; - bool isInfinite = 0; - int opt; - benchmark = 0; // Default, means don't do any benchmarks. - Cinfo::rebuildOpIndex(); + unsigned int numCores = getNumCores(); + int numNodes = 1; + int myNode = 0; + bool isInfinite = 0; + int opt; + benchmark = 0; // Default, means don't do any benchmarks. + Cinfo::rebuildOpIndex(); #ifdef USE_MPI - /* - // OpenMPI does not use argc or argv. - // unsigned int temp_argc = 1; - int provided; - MPI_Init_thread( &argc, &argv, MPI_THREAD_SERIALIZED, &provided ); - */ - MPI_Init( &argc, &argv ); - - MPI_Comm_size( MPI_COMM_WORLD, &numNodes ); - MPI_Comm_rank( MPI_COMM_WORLD, &myNode ); - /* - if ( provided < MPI_THREAD_SERIALIZED && myNode == 0 ) { - cout << "Warning: This MPI implementation does not like multithreading: " << provided << "\n"; - } - */ - // myNode = MPI::COMM_WORLD.Get_rank(); + /* + // OpenMPI does not use argc or argv. + // unsigned int temp_argc = 1; + int provided; + MPI_Init_thread( &argc, &argv, MPI_THREAD_SERIALIZED, &provided ); + */ + MPI_Init( &argc, &argv ); + + MPI_Comm_size( MPI_COMM_WORLD, &numNodes ); + MPI_Comm_rank( MPI_COMM_WORLD, &myNode ); + /* + if ( provided < MPI_THREAD_SERIALIZED && myNode == 0 ) { + cout << "Warning: This MPI implementation does not like multithreading: " << provided << "\n"; + } + */ + // myNode = MPI::COMM_WORLD.Get_rank(); #endif - /** - * Here we allow the user to override the automatic identification - * of processor configuration - */ - while ( ( opt = getopt( argc, argv, "hiqurn:b:B:" ) ) != -1 ) { - switch ( opt ) { - case 'i' : // infinite loop, used for multinode debugging, to give gdb something to attach to. - isInfinite = 1; - break; - case 'n': // Multiple nodes - numNodes = (unsigned int)atoi( optarg ); - break; - case 'b': // Benchmark: - benchmark = atoi( optarg ); - break; - case 'B': // Benchmark plus dump data: handle later. - break; - case 'u': // Do unit tests, pass back. - doUnitTests = 1; - break; - case 'r': // Do regression tests: pass back - doRegressionTests = 1; - break; - case 'q': // quit immediately after completion. - quitFlag = 1; - break; - case 'h': // help - default: - cout << "Usage: moose -help -infiniteLoop -unit_tests -regression_tests -quit -n numNodes -benchmark [ksolve intFire hhNet msg_<msgType>_<size>]\n"; - - exit( 1 ); - } - } - if ( myNode == 0 ) + /** + * Here we allow the user to override the automatic identification + * of processor configuration + */ + while ( ( opt = getopt( argc, argv, "hiqurn:b:B:" ) ) != -1 ) + { + switch ( opt ) { -#ifndef QUIET_MODE - cout << "on node " << myNode << ", numNodes = " - << numNodes << ", numCores = " << numCores << endl; -#endif + case 'i' : // infinite loop, used for multinode debugging, to give gdb something to attach to. + isInfinite = 1; + break; + case 'n': // Multiple nodes + numNodes = (unsigned int)atoi( optarg ); + break; + case 'b': // Benchmark: + benchmark = atoi( optarg ); + break; + case 'B': // Benchmark plus dump data: handle later. + break; + case 'u': // Do unit tests, pass back. + doUnitTests = 1; + break; + case 'r': // Do regression tests: pass back + doRegressionTests = 1; + break; + case 'q': // quit immediately after completion. + quitFlag = 1; + break; + case 'h': // help + default: + cout << "Usage: moose -help -infiniteLoop -unit_tests -regression_tests -quit -n numNodes -benchmark [ksolve intFire hhNet msg_<msgType>_<size>]\n"; + + exit( 1 ); } + } + if ( myNode == 0 ) + { + +#if 0 + cout << "on node " << myNode << ", numNodes = " + << numNodes << ", numCores = " << numCores << endl; +#endif + } - Id shellId; - Element* shelle = - new GlobalDataElement( shellId, Shell::initCinfo(), "root", 1 ); + Id shellId; + Element* shelle = + new GlobalDataElement( shellId, Shell::initCinfo(), "root", 1 ); - Id clockId = Id::nextId(); - assert( clockId.value() == 1 ); - Id classMasterId = Id::nextId(); - Id postMasterId = Id::nextId(); + Id clockId = Id::nextId(); + assert( clockId.value() == 1 ); + Id classMasterId = Id::nextId(); + Id postMasterId = Id::nextId(); - Shell* s = reinterpret_cast< Shell* >( shellId.eref().data() ); - s->setShellElement( shelle ); - s->setHardware( numCores, numNodes, myNode ); - s->loadBalance(); + Shell* s = reinterpret_cast< Shell* >( shellId.eref().data() ); + s->setShellElement( shelle ); + s->setHardware( numCores, numNodes, myNode ); + s->loadBalance(); - /// Sets up the Elements that represent each class of Msg. - unsigned int numMsg = Msg::initMsgManagers(); + /// Sets up the Elements that represent each class of Msg. + unsigned int numMsg = Msg::initMsgManagers(); - new GlobalDataElement( clockId, Clock::initCinfo(), "clock", 1 ); - new GlobalDataElement( classMasterId, Neutral::initCinfo(), "classes", 1); - new GlobalDataElement( postMasterId, PostMaster::initCinfo(), "postmaster", 1 ); + new GlobalDataElement( clockId, Clock::initCinfo(), "clock", 1 ); + new GlobalDataElement( classMasterId, Neutral::initCinfo(), "classes", 1); + new GlobalDataElement( postMasterId, PostMaster::initCinfo(), "postmaster", 1 ); - assert ( shellId == Id() ); - assert( clockId == Id( 1 ) ); - assert( classMasterId == Id( 2 ) ); - assert( postMasterId == Id( 3 ) ); + assert ( shellId == Id() ); + assert( clockId == Id( 1 ) ); + assert( classMasterId == Id( 2 ) ); + assert( postMasterId == Id( 3 ) ); - // s->connectMasterMsg(); + // s->connectMasterMsg(); - Shell::adopt( shellId, clockId, numMsg++ ); - Shell::adopt( shellId, classMasterId, numMsg++ ); - Shell::adopt( shellId, postMasterId, numMsg++ ); + Shell::adopt( shellId, clockId, numMsg++ ); + Shell::adopt( shellId, classMasterId, numMsg++ ); + Shell::adopt( shellId, postMasterId, numMsg++ ); - assert( numMsg == 10 ); // Must be the same on all nodes. + assert( numMsg == 10 ); // Must be the same on all nodes. - Cinfo::makeCinfoElements( classMasterId ); + Cinfo::makeCinfoElements( classMasterId ); - // This will be initialized within the Process loop, and better there - // as it flags attempts to call the Reduce operations before ProcessLoop - // Qinfo::clearReduceQ( numCores ); // Initialize the ReduceQ entry. + // This will be initialized within the Process loop, and better there + // as it flags attempts to call the Reduce operations before ProcessLoop + // Qinfo::clearReduceQ( numCores ); // Initialize the ReduceQ entry. - // SetGet::setShell(); - // Msg* m = new OneToOneMsg( shelle, shelle ); - // assert ( m != 0 ); + // SetGet::setShell(); + // Msg* m = new OneToOneMsg( shelle, shelle ); + // assert ( m != 0 ); - while ( isInfinite ) // busy loop for debugging under gdb and MPI. - ; + while ( isInfinite ) // busy loop for debugging under gdb and MPI. + ; - return shellId; + return shellId; } /** @@ -270,28 +273,29 @@ Id init( int argc, char** argv, bool& doUnitTests, bool& doRegressionTests, void nonMpiTests( Shell* s ) { #ifdef DO_UNIT_TESTS - if ( Shell::myNode() == 0 ) { - unsigned int numNodes = s->numNodes(); - unsigned int numCores = s->numCores(); - if ( numCores > 0 ) - s->setHardware( 1, 1, 0 ); - testAsync(); - testMsg(); - testShell(); - testScheduling(); - testBuiltins(); - // testKinetics(); - // testKineticSolvers(); - testBiophysics(); - // testHSolve(); - // testGeom(); - // testMesh(); - // testSigNeur(); + if ( Shell::myNode() == 0 ) + { + unsigned int numNodes = s->numNodes(); + unsigned int numCores = s->numCores(); + if ( numCores > 0 ) + s->setHardware( 1, 1, 0 ); + testAsync(); + testMsg(); + testShell(); + testScheduling(); + testBuiltins(); + // testKinetics(); + // testKineticSolvers(); + testBiophysics(); + // testHSolve(); + // testGeom(); + // testMesh(); + // testSigNeur(); #ifdef USE_SMOLDYN - // testSmoldyn(); + // testSmoldyn(); #endif - s->setHardware( numCores, numNodes, 0 ); - } + s->setHardware( numCores, numNodes, 0 ); + } #endif } @@ -302,13 +306,13 @@ void nonMpiTests( Shell* s ) void processTests( Shell* s ) { #ifdef DO_UNIT_TESTS - testSchedulingProcess(); - testBuiltinsProcess(); - // testKineticsProcess(); - testBiophysicsProcess(); - // testKineticSolversProcess(); - // testSimManager(); - // testSigNeurProcess(); + testSchedulingProcess(); + testBuiltinsProcess(); + // testKineticsProcess(); + testBiophysicsProcess(); + // testKineticSolversProcess(); + // testSimManager(); + // testSigNeurProcess(); #endif } @@ -319,13 +323,13 @@ void processTests( Shell* s ) void mpiTests() { #ifdef DO_UNIT_TESTS - testMpiMsg(); - cout << "." << flush; - testMpiShell(); - cout << "." << flush; - testMpiBuiltins(); - cout << "." << flush; - testMpiScheduling(); - cout << "." << flush; + testMpiMsg(); + cout << "." << flush; + testMpiShell(); + cout << "." << flush; + testMpiBuiltins(); + cout << "." << flush; + testMpiScheduling(); + cout << "." << flush; #endif } diff --git a/python/moose/utils.py b/python/moose/utils.py index 9abf7ae5..8dbec98a 100644 --- a/python/moose/utils.py +++ b/python/moose/utils.py @@ -1,12 +1,7 @@ -# -*- coding: utf-8 -*- -# utils.py: -# +# -* coding: utf-8 -*- # Utility functions for moose. -# -# NOTE: Some function might break because unicode is default string in python3. from __future__ import print_function, division, absolute_import - from __future__ import print_function, division from __future__ import absolute_import @@ -27,9 +22,7 @@ from moose.print_utils import * try: from moose.plot_utils import * except Exception as e: - info( "Plot utilities are not loaded due to '%s'" ) - pass - + info( "Plot utilities are not loaded due to '%s'" % e ) def create_table_path(model, graph, element, field): @@ -849,7 +842,6 @@ def get_child_Mstring(mooseobject,mstring): return child return None -# Note: This function is also moved to helper.moose_methods def connect_CaConc(compartment_list, temperature=None): """ Connect the Ca pools and channels within each of the compartments in compartment_list Ca channels should have a child Mstring named 'ion' with value set in MOOSE. @@ -917,112 +909,3 @@ def connect_CaConc(compartment_list, temperature=None): #print 'Connected concOut of',caconc.path,'to concen of',channel.path except TypeError: pass - -############# added by Aditya Gilra -- end ################ -import uuid -import unittest -import sys -from io import StringIO as _sio - -class _TestMooseUtils(unittest.TestCase): - def test_printtree(self): - s = moose.Neutral('/cell') - soma = moose.Neutral('%s/soma'% (s.path)) - d1 = moose.Neutral('%s/d1'% (soma.path)) - d2 = moose.Neutral('%s/d2'% (soma.path)) - d3 = moose.Neutral('%s/d3'% (d1.path)) - d4 = moose.Neutral('%s/d4'% (d1.path)) - d5 = moose.Neutral('%s/d5'% (s.path)) - orig_stdout = sys.stdout - sys.stdout = _sio() - printtree(s) - expected = """ -cell -| -|__ soma -| | -| |__ d1 -| | | -| | |__ d3 -| | | -| | |__ d4 -| | -| |__ d2 -| -|__ d5 -""" - self.assertEqual(sys.stdout.getvalue(), expected) - sys.stdout = _sio() - s1 = moose.Neutral('cell1') - c1 = moose.Neutral('%s/c1' % (s1.path)) - c2 = moose.Neutral('%s/c2' % (c1.path)) - c3 = moose.Neutral('%s/c3' % (c1.path)) - c4 = moose.Neutral('%s/c4' % (c2.path)) - c5 = moose.Neutral('%s/c5' % (c3.path)) - c6 = moose.Neutral('%s/c6' % (c3.path)) - c7 = moose.Neutral('%s/c7' % (c4.path)) - c8 = moose.Neutral('%s/c8' % (c5.path)) - printtree(s1) - expected1 = """ -cell1 -| -|__ c1 - | - |__ c2 - | | - | |__ c4 - | | - | |__ c7 - | - |__ c3 - | - |__ c5 - | | - | |__ c8 - | - |__ c6 -""" - self.assertEqual(sys.stdout.getvalue(), expected1) - - def test_autoposition(self): - """Simple check for automatic generation of positions. - - A spherical soma is created with 20 um diameter. A 100 - compartment cable is created attached to it with each - compartment of length 100 um. - - """ - testid = 'test%s' % (uuid.uuid4()) - container = moose.Neutral('/test') - model = moose.Neuron('/test/%s' % (testid)) - soma = moose.Compartment('%s/soma' % (model.path)) - soma.diameter = 20e-6 - soma.length = 0.0 - parent = soma - comps = [] - for ii in range(100): - comp = moose.Compartment('%s/comp_%d' % (model.path, ii)) - comp.diameter = 10e-6 - comp.length = 100e-6 - moose.connect(parent, 'raxial', comp, 'axial') - comps.append(comp) - parent = comp - soma = autoposition(model) - sigfig = 8 - self.assertAlmostEqual(soma.x0, 0.0, sigfig) - self.assertAlmostEqual(soma.y0, 0.0, sigfig) - self.assertAlmostEqual(soma.z0, 0.0, sigfig) - self.assertAlmostEqual(soma.x, 0.0, sigfig) - self.assertAlmostEqual(soma.y, 0.0, sigfig) - self.assertAlmostEqual(soma.z, soma.diameter/2.0, sigfig) - for ii, comp in enumerate(comps): - print(comp.path, ii) - self.assertAlmostEqual(comp.x0, 0, sigfig) - self.assertAlmostEqual(comp.y0, 0.0, sigfig) - self.assertAlmostEqual(comp.z0, soma.diameter/2.0 + ii * 100e-6, sigfig) - self.assertAlmostEqual(comp.x, 0.0, sigfig) - self.assertAlmostEqual(comp.y, 0.0, sigfig) - self.assertAlmostEqual(comp.z, soma.diameter/2.0 + (ii + 1) * 100e-6, sigfig) - -if __name__ == "__main__": # test printtree - unittest.main() diff --git a/python/rdesigneur/rdesigneur.py b/python/rdesigneur/rdesigneur.py index aa5b3fce..568bac94 100644 --- a/python/rdesigneur/rdesigneur.py +++ b/python/rdesigneur/rdesigneur.py @@ -29,12 +29,7 @@ import matplotlib.pyplot as plt import rdesigneur.rmoogli as rmoogli from rdesigneur.rdesigneurProtos import * -from moose.fixXreacs import fixXreacs -#import fixXreacs -#from . import fixXreacs -#from rdesigneur.rmoogli import * -#import rmoogli -#from rdesigneurProtos import * +import moose.fixXreacs as fixXreacs from moose.neuroml.NeuroML import NeuroML from moose.neuroml.ChannelML import ChannelML @@ -101,8 +96,8 @@ class rdesigneur: chemDistrib = [], adaptorList= [], stimList = [], - plotList = [], # elecpath, geom_expr, object, field, title ['wave' [min max]] - moogList = [], + plotList = [], + moogList = [], params = None ): """ Constructor of the rdesigner. This just sets up internal fields @@ -143,16 +138,11 @@ class rdesigneur: self.params = params self.adaptorList = adaptorList - try: - self.stimList = [ rstim.convertArg(i) for i in stimList ] - self.plotList = [ rplot.convertArg(i) for i in plotList ] - self.moogList = [ rmoog.convertArg(i) for i in moogList ] - except BuildError as msg: - print("Error: rdesigneur: " + msg) - quit() - - #self.saveList = plotList #ADDED BY Sarthak + self.stimList = stimList + self.plotList = plotList + self.saveList = plotList #ADDED BY Sarthak self.saveAs = [] + self.moogList = moogList self.plotNames = [] self.wavePlotNames = [] self.saveNames = [] @@ -174,19 +164,18 @@ class rdesigneur: quit() - ################################################################ def _printModelStats( self ): - print("Rdesigneur: Elec model has", + print("\n\tRdesigneur: Elec model has", self.elecid.numCompartments, "compartments and", self.elecid.numSpines, "spines on", len( self.cellPortionElist ), "compartments.") if hasattr( self , 'chemid' ): dmstoich = moose.element( self.dendCompt.path + '/stoich' ) - print("Chem part of model has the following compartments: ") + print("\tChem part of model has the following compartments: ") for j in moose.wildcardFind( '/model/chem/##[ISA=ChemCompt]'): s = moose.element( j.path + '/stoich' ) - print( "In {}, {} voxels X {} pools".format( j.name, j.mesh.num, s.numAllPools ) ) + print( "\t | In {}, {} voxels X {} pools".format( j.name, j.mesh.num, s.numAllPools ) ) def buildModel( self, modelPath = '/model' ): if moose.exists( modelPath ): @@ -195,28 +184,30 @@ class rdesigneur: return self.model = moose.Neutral( modelPath ) self.modelPath = modelPath - try: - # Protos made in the init phase. Now install the elec and - # chem protos on model. - self.installCellFromProtos() - # Now assign all the distributions - self.buildPassiveDistrib() - self.buildChanDistrib() - self.buildSpineDistrib() - self.buildChemDistrib() - self._configureSolvers() - self.buildAdaptors() - self._buildStims() - self._buildPlots() - self._buildMoogli() - self._configureHSolve() - self._configureClocks() + print( "[INFO ] rdesigneur: Building model. " ) + funcs = [ self.installCellFromProtos, self.buildPassiveDistrib + , self.buildChanDistrib, self.buildSpineDistrib, self.buildChemDistrib + , self._configureSolvers, self.buildAdaptors, self._buildStims + , self._buildPlots, self._buildMoogli, self._configureHSolve + , self._configureClocks, self._printModelStats, self._savePlots + ] + for i, _func in enumerate( funcs ): if self.verbose: - self._printModelStats() - - except BuildError as msg: - print("Error: rdesigneur: model build failed:", msg) - moose.delete( self.model ) + print( " + (%d/%d) executing %25s"%(i, len(funcs), _func.__name__), end=' ' ) + sys.stdout.flush() + t0 = time.time() + try: + _func( ) + except BuildError as msg: + print("Error: rdesigneur: model build failed:", msg) + moose.delete( self.model ) + return + t = time.time() - t0 + if self.verbose: + msg = r' ... DONE' + if t > 1: + msg += ' %.3f sec' % t + print( msg ) def installCellFromProtos( self ): if self.stealCellFromLibrary: @@ -257,11 +248,14 @@ class rdesigneur: if bracePos == -1: return False - modPos = func.find( "." ) + # . can be in path name as well. Find the last dot which is most likely + # to be the function name. + modPos = func.rfind( "." ) if ( modPos != -1 ): # Function is in a file, load and check - pathTokens = func[0:modPos].split('/') + resolvedPath = os.path.realpath( func[0:modPos] ) + pathTokens = resolvedPath.split('/') pathTokens = ['/'] + pathTokens - modulePath = os.path.join(*pathTokens[:-1]) + modulePath = os.path.realpath(os.path.join(*pathTokens[:-1])) moduleName = pathTokens[-1] funcName = func[modPos+1:bracePos] moduleFile, pathName, description = imp.find_module(moduleName, [modulePath]) @@ -469,10 +463,10 @@ class rdesigneur: # Here we hack geomExpr to use it for the syn weight. We assume it # is just a number. In due course # it should be possible to actually evaluate it according to geom. - synWeight = float( stimInfo.geom_expr ) + synWeight = float( stimInfo[1] ) stimObj = [] for i in dendCompts + spineCompts: - path = i.path + '/' + stimInfo.relpath + '/sh/synapse[0]' + path = i.path + '/' + stimInfo[2] + '/sh/synapse[0]' if moose.exists( path ): synInput = make_synInput( name='synInput', parent=path ) synInput.doPeriodic = doPeriodic @@ -492,10 +486,6 @@ class rdesigneur: # Expression can use p, g, L, len, dia, maxP, maxG, maxL. temp = [] for i in self.passiveDistrib: - if (len( i ) < 3) or (len(i) %2 != 1): - raise BuildError( "buildPassiveDistrib: Need 3 + N*2 arguments, have {}".format( len(i) ) ) - - temp.append( '.' ) temp.extend( i ) temp.extend( [""] ) self.elecid.passiveDistribution = temp @@ -609,24 +599,27 @@ class rdesigneur: # Utility function for doing lookups for objects. def _makeUniqueNameStr( self, obj ): - return obj.name + " " + str( obj.index ) + # second one is faster than the former. 140 ns v/s 180 ns. + # return obj.name + " " + str( obj.index ) + return "%s %s" % (obj.name, obj.index) # Returns vector of source objects, and the field to use. # plotSpec is of the form # [ region_wildcard, region_expr, path, field, title] def _parseComptField( self, comptList, plotSpec, knownFields ): # Put in stuff to go through fields if the target is a chem object - field = plotSpec.field + field = plotSpec[3] if not field in knownFields: print("Warning: Rdesigneur::_parseComptField: Unknown field '{}'".format( field ) ) return (), "" kf = knownFields[field] # Find the field to decide type. - if ( kf[0] == 'CaConcBase' or kf[0] == 'ChanBase' or kf[0] == 'NMDAChan' or kf[0] == 'VClamp' ): - objList = self._collapseElistToPathAndClass( comptList, plotSpec.relpath, kf[0] ) + if kf[0] in ['CaConcBase', 'ChanBase', 'NMDAChan', 'VClamp']: + objList = self._collapseElistToPathAndClass( comptList, plotSpec[2], kf[0] ) return objList, kf[1] - elif (field == 'n' or field == 'conc' or field == 'volume' ): - path = plotSpec.relpath + + elif field in [ 'n', 'conc', 'volume']: + path = plotSpec[2] pos = path.find( '/' ) if pos == -1: # Assume it is in the dend compartment. path = 'dend/' + path @@ -647,13 +640,13 @@ class rdesigneur: voxelVec = [i for i in range(len( em ) ) if em[i] in comptSet ] # Here we collapse the voxelVec into objects to plot. - allObj = moose.vec( self.modelPath + '/chem/' + plotSpec.relpath ) + allObj = moose.vec( self.modelPath + '/chem/' + plotSpec[2] ) #print "####### allObj=", self.modelPath + '/chem/' + plotSpec[2] if len( allObj ) >= len( voxelVec ): objList = [ allObj[int(j)] for j in voxelVec] else: objList = [] - print( "Warning: Rdesigneur::_parseComptField: unknown Object: '", plotSpec.relpath, "'" ) + print( "Warn: Rdesigneur::_parseComptField: unknown Object: '%s'" % plotSpec[2] ) #print "############", chemCompt, len(objList), kf[1] return objList, kf[1] @@ -685,7 +678,7 @@ class rdesigneur: dummy = moose.element( '/' ) k = 0 for i in self.plotList: - pair = i.elecpath + ' ' + i.geom_expr + pair = i[0] + " " + i[1] dendCompts = self.elecid.compartmentsFromExpression[ pair ] spineCompts = self.elecid.spinesFromExpression[ pair ] plotObj, plotField = self._parseComptField( dendCompts, i, knownFields ) @@ -693,32 +686,32 @@ class rdesigneur: assert( plotField == plotField2 ) plotObj3 = plotObj + plotObj2 numPlots = sum( q != dummy for q in plotObj3 ) - #print( "PlotList: {0}: numobj={1}, field ={2}, nd={3}, ns={4}".format( pair, numPlots, plotField, len( dendCompts ), len( spineCompts ) ) ) - if numPlots > 0: - tabname = graphs.path + '/plot' + str(k) - scale = knownFields[i.field][2] - units = knownFields[i.field][3] - if i.mode == 'wave': - self.wavePlotNames.append( [ tabname, i.title, k, scale, units, i.field, i.ymin, i.ymax ] ) - else: - self.plotNames.append( [ tabname, i.title, k, scale, units, i.field, i.ymin, i.ymax ] ) - if len( i.saveFile ) > 4 and i.saveFile[-4] == '.xml' or i.saveFile: - self.saveNames.append( [ tabname, len(self.saveNames), scale, units, i ] ) + if numPlots == 0: + return + + tabname = graphs.path + '/plot' + str(k) + scale = knownFields[i[3]][2] + units = knownFields[i[3]][3] + ymin = i[6] if len(i) > 7 else 0 + ymax = i[7] if len(i) > 7 else 0 + if len( i ) > 5 and i[5] == 'wave': + self.wavePlotNames.append( [ tabname, i[4], k, scale, units, i[3], ymin, ymax ] ) + else: + self.plotNames.append( [ tabname, i[4], k, scale, units, i[3], ymin, ymax ] ) + k += 1 + if i[3] in [ 'n', 'conc', 'volume', 'Gbar' ]: + tabs = moose.Table2( tabname, numPlots ) + else: + tabs = moose.Table( tabname, numPlots ) + if i[3] == 'spikeTime': + tabs.vec.threshold = -0.02 # Threshold for classifying Vm as a spike. + tabs.vec.useSpikeMode = True # spike detect mode on - k += 1 - if i.field == 'n' or i.field == 'conc' or i.field == 'volume' or i.field == 'Gbar': - tabs = moose.Table2( tabname, numPlots ) - else: - tabs = moose.Table( tabname, numPlots ) - if i.field == 'spikeTime': - tabs.vec.threshold = -0.02 # Threshold for classifying Vm as a spike. - tabs.vec.useSpikeMode = True # spike detect mode on - - vtabs = moose.vec( tabs ) - q = 0 - for p in [ x for x in plotObj3 if x != dummy ]: - moose.connect( vtabs[q], 'requestOut', p, plotField ) - q += 1 + vtabs = moose.vec( tabs ) + q = 0 + for p in [ x for x in plotObj3 if x != dummy ]: + moose.connect( vtabs[q], 'requestOut', p, plotField ) + q += 1 def _buildMoogli( self ): knownFields = { @@ -738,8 +731,8 @@ class rdesigneur: moogliBase = moose.Neutral( self.modelPath + '/moogli' ) k = 0 for i in self.moogList: - kf = knownFields[i.field] - pair = i.elecpath + " " + i.geom_expr + kf = knownFields[i[3]] + pair = i[0] + " " + i[1] dendCompts = self.elecid.compartmentsFromExpression[ pair ] spineCompts = self.elecid.spinesFromExpression[ pair ] dendObj, mooField = self._parseComptField( dendCompts, i, knownFields ) @@ -747,6 +740,13 @@ class rdesigneur: assert( mooField == mooField2 ) mooObj3 = dendObj + spineObj numMoogli = len( mooObj3 ) + #dendComptMap = self.dendCompt.elecComptMap + #self.moogliViewer = rmoogli.makeMoogli( self, mooObj3, mooField ) + if len( i ) == 5: + i.extend( kf[4:6] ) + elif len( i ) == 6: + i.extend( [kf[5]] ) + #self.moogliViewer = rmoogli.makeMoogli( self, mooObj3, i, kf ) self.moogNames.append( rmoogli.makeMoogli( self, mooObj3, i, kf ) ) @@ -770,7 +770,6 @@ rdesigneur.rmoogli.updateMoogliViewer() def display( self, startIndex = 0 ): for i in self.plotNames: - # ?, title, fignum, scale, ylabel, wave/spikeTime, ymin, ymax plt.figure( i[2] + startIndex ) plt.title( i[1] ) plt.xlabel( "Time (s)" ) @@ -788,8 +787,6 @@ rdesigneur.rmoogli.updateMoogliViewer() t = np.arange( 0, vtab[0].vector.size, 1 ) * vtab[0].dt for j in vtab: plt.plot( t, j.vector * i[3] ) - if i[6] != i[7]: - plt.ylim( i[6], i[7] ) if len( self.moogList ) or len( self.wavePlotNames ) > 0: plt.ion() # Here we build the plots and lines for the waveplots @@ -818,14 +815,10 @@ rdesigneur.rmoogli.updateMoogliViewer() plt.title( i[1] ) plt.xlabel( "position (voxels)" ) plt.ylabel( i[4] ) - if i[6] != i[7]: - mn = i[6] - mx = i[7] - else: - mn = np.min(vpts) - mx = np.max(vpts) - if mn/mx < 0.3: - mn = 0 + mn = np.min(vpts) + mx = np.max(vpts) + if mn/mx < 0.3: + mn = 0 ax.set_ylim( mn, mx ) line, = plt.plot( range( len( vtab ) ), vpts[0] ) timeLabel = plt.text( len(vtab ) * 0.05, mn + 0.9*(mx-mn), 'time = 0' ) @@ -835,7 +828,7 @@ rdesigneur.rmoogli.updateMoogliViewer() def displayWavePlots( self ): for f in range( self.numWaveFrames ): for i in self.wavePlotNames: - wp = i[-1] + wp = i[6] if len( wp[2] ) > f: wp[1].set_ydata( wp[2][f] ) wp[3].set_text( "time = {:.1f}".format(f*self.frameDt) ) @@ -854,13 +847,134 @@ rdesigneur.rmoogli.updateMoogliViewer() Email address: sarthaks442@gmail.com Heavily modified by U.S. Bhalla ''' - def _writeXML( self, plotData, time, vtab ): - tabname = plotData[0] - idx = plotData[1] - scale = plotData[2] - units = plotData[3] - rp = plotData[4] - filename = rp.saveFile[:-4] + str(idx) + '.xml' + + def _savePlots( self ): + if self.verbose: + print( 'rdesigneur: Saving plots ...', end = ' ' ) + sys.stdout.flush() + + knownFields = { + 'Vm':('CompartmentBase', 'getVm', 1000, 'Memb. Potential (mV)' ), + 'Cm':('CompartmentBase', 'getCm', 1e12, 'Memb. capacitance (pF)' ), + 'Rm':('CompartmentBase', 'getRm', 1e-9, 'Memb. Res (GOhm)' ), + 'Ra':('CompartmentBase', 'getRa', 1e-6, 'Axial Res (MOhm)' ), + 'spikeTime':('CompartmentBase', 'getVm', 1, 'Spike Times (s)'), + 'Im':('CompartmentBase', 'getIm', 1e9, 'Memb. current (nA)' ), + 'inject':('CompartmentBase', 'getInject', 1e9, 'inject current (nA)' ), + 'Gbar':('ChanBase', 'getGbar', 1e9, 'chan max conductance (nS)' ), + 'modulation':('ChanBase', 'getModulation', 1, 'chan modulation (unitless)' ), + 'Gk':('ChanBase', 'getGk', 1e9, 'chan conductance (nS)' ), + 'Ik':('ChanBase', 'getIk', 1e9, 'chan current (nA)' ), + 'ICa':('NMDAChan', 'getICa', 1e9, 'Ca current (nA)' ), + 'Ca':('CaConcBase', 'getCa', 1e3, 'Ca conc (uM)' ), + 'n':('PoolBase', 'getN', 1, '# of molecules'), + 'conc':('PoolBase', 'getConc', 1000, 'Concentration (uM)' ), + 'volume':('PoolBase', 'getVolume', 1e18, 'Volume (um^3)' ), + 'current':('VClamp', 'getCurrent', 1e9, 'Holding Current (nA)') + } + + save_graphs = moose.Neutral( self.modelPath + '/save_graphs' ) + dummy = moose.element( '/' ) + k = 0 + + for i in self.saveList: + pair = i[0] + " " + i[1] + dendCompts = self.elecid.compartmentsFromExpression[ pair ] + spineCompts = self.elecid.spinesFromExpression[ pair ] + plotObj, plotField = self._parseComptField( dendCompts, i, knownFields ) + plotObj2, plotField2 = self._parseComptField( spineCompts, i, knownFields ) + assert( plotField == plotField2 ) + plotObj3 = plotObj + plotObj2 + numPlots = sum( i != dummy for i in plotObj3 ) + if numPlots > 0: + save_tabname = save_graphs.path + '/save_plot' + str(k) + scale = knownFields[i[3]][2] + units = knownFields[i[3]][3] + self.saveNames.append( ( save_tabname, i[4], k, scale, units ) ) + k += 1 + if i[3] in [ 'n', 'conc', 'volume', 'Gbar' ]: + save_tabs = moose.Table2( save_tabname, numPlots ) + save_vtabs = moose.vec( save_tabs ) + else: + save_tabs = moose.Table( save_tabname, numPlots ) + save_vtabs = moose.vec( save_tabs ) + if i[3] == 'spikeTime': + save_vtabs.threshold = -0.02 # Threshold for classifying Vm as a spike. + save_vtabs.useSpikeMode = True # spike detect mode on + q = 0 + for p in [ x for x in plotObj3 if x != dummy ]: + moose.connect( save_vtabs[q], 'requestOut', p, plotField ) + q += 1 + + if self.verbose: + print( ' ... DONE.' ) + + def _getTimeSeriesTable( self ): + + ''' + This function gets the list with all the details of the simulation + required for plotting. + This function adds flexibility in terms of the details + we wish to store. + ''' + + knownFields = { + 'Vm':('CompartmentBase', 'getVm', 1000, 'Memb. Potential (mV)' ), + 'spikeTime':('CompartmentBase', 'getVm', 1, 'Spike Times (s)'), + 'Im':('CompartmentBase', 'getIm', 1e9, 'Memb. current (nA)' ), + 'inject':('CompartmentBase', 'getInject', 1e9, 'inject current (nA)' ), + 'Gbar':('ChanBase', 'getGbar', 1e9, 'chan max conductance (nS)' ), + 'Gk':('ChanBase', 'getGk', 1e9, 'chan conductance (nS)' ), + 'Ik':('ChanBase', 'getIk', 1e9, 'chan current (nA)' ), + 'ICa':('NMDAChan', 'getICa', 1e9, 'Ca current (nA)' ), + 'Ca':('CaConcBase', 'getCa', 1e3, 'Ca conc (uM)' ), + 'n':('PoolBase', 'getN', 1, '# of molecules'), + 'conc':('PoolBase', 'getConc', 1000, 'Concentration (uM)' ), + 'volume':('PoolBase', 'getVolume', 1e18, 'Volume (um^3)' ) + } + + ''' + This takes data from plotList + saveList is exactly like plotList but with a few additional arguments: + ->It will have a resolution option, i.e., the number of decimal figures to which the value should be rounded + ->There is a list of "saveAs" formats + With saveList, the user will able to set what all details he wishes to be saved. + ''' + + for i,ind in enumerate(self.saveNames): + pair = self.saveList[i][0] + " " + self.saveList[i][1] + dendCompts = self.elecid.compartmentsFromExpression[ pair ] + spineCompts = self.elecid.spinesFromExpression[ pair ] + # Here we get the object details from plotList + savePlotObj, plotField = self._parseComptField( dendCompts, self.saveList[i], knownFields ) + savePlotObj2, plotField2 = self._parseComptField( spineCompts, self.saveList[i], knownFields ) + savePlotObj3 = savePlotObj + savePlotObj2 + + rowList = list(ind) + save_vtab = moose.vec( ind[0] ) + t = np.arange( 0, save_vtab[0].vector.size, 1 ) * save_vtab[0].dt + + rowList.append(save_vtab[0].dt) + rowList.append(t) + rowList.append([jvec.vector * ind[3] for jvec in save_vtab]) #get values + rowList.append(self.saveList[i][3]) + rowList.append(filter(lambda obj: obj.path != '/', savePlotObj3)) #this filters out dummy elements + + if (type(self.saveList[i][-1])==int): + rowList.append(self.saveList[i][-1]) + else: + rowList.append(12) + + self.tabForXML.append(rowList) + rowList = [] + + timeSeriesTable = self.tabForXML # the list with all the details of plot + return timeSeriesTable + + def _writeXML( self, filename, timeSeriesData ): #to write to XML file + + plotData = timeSeriesData + print("[CAUTION] The '%s' file might be very large if all the compartments are to be saved." % filename) root = etree.Element("TimeSeriesPlot") parameters = etree.SubElement( root, "parameters" ) if self.params == None: @@ -873,33 +987,36 @@ rdesigneur.rmoogli.updateMoogliViewer() #plotData contains all the details of a single plot title = etree.SubElement( root, "timeSeries" ) - title.set( 'title', rp.title) - title.set( 'field', rp.field) - title.set( 'scale', str(scale) ) - title.set( 'units', units) - title.set( 'dt', str(vtab[0].dt) ) - res = rp.saveResolution + title.set( 'title', str(plotData[1])) + title.set( 'field', str(plotData[8])) + title.set( 'scale', str(plotData[3])) + title.set( 'units', str(plotData[4])) + title.set( 'dt', str(plotData[5])) p = [] - for t, v in zip( time, vtab ): + assert(len(plotData[7]) == len(plotData[9])) + + res = plotData[10] + for ind, jvec in enumerate(plotData[7]): p.append( etree.SubElement( title, "data")) - p[-1].set( 'path', v.path ) - p[-1].text = ''.join( str(round(y,res)) + ' ' for y in v.vector ) + p[-1].set( 'path', str(plotData[9][ind].path)) + p[-1].text = ''.join( str(round(value,res)) + ' ' for value in jvec ) tree = etree.ElementTree(root) tree.write(filename) - def _writeCSV( self, plotData, time, vtab ): - tabname = plotData[0] - idx = plotData[1] - scale = plotData[2] - units = plotData[3] - rp = plotData[4] - filename = rp.saveFile[:-4] + str(idx) + '.csv' - - header = ["time",] - valMatrix = [time,] - header.extend( [ v.path for v in vtab ] ) - valMatrix.extend( [ v.vector for v in vtab ] ) - nv = np.array( valMatrix ).T + def _writeCSV(self, filename, timeSeriesData): + + plotData = timeSeriesData + dataList = [] + header = [] + time = plotData[6] + res = plotData[10] + + for ind, jvec in enumerate(plotData[7]): + header.append(plotData[9][ind].path) + dataList.append([round(value,res) for value in jvec.tolist()]) + dl = [tuple(lst) for lst in dataList] + rows = zip(tuple(time), *dl) + header.insert(0, "time") with open(filename, 'wb') as f: writer = csv.writer(f, quoting=csv.QUOTE_MINIMAL) writer.writerow(header) @@ -907,25 +1024,42 @@ rdesigneur.rmoogli.updateMoogliViewer() writer.writerow(row) ##########****SAVING*****############### + def _saveFormats(self, timeSeriesData, k, *filenames): + "This takes in the filenames and writes to corresponding format." + if filenames: + for filename in filenames: + for name in filename: + print (name) + if name[-4:] == '.xml': + self._writeXML(name, timeSeriesData) + print(name, " written") + elif name[-4:] == '.csv': + self._writeCSV(name, timeSeriesData) + print(name, " written") + else: + print("Save format not known") + pass + else: + pass def _save( self ): - for i in self.saveNames: - tabname = i[0] - idx = i[1] - scale = i[2] - units = i[3] - rp = i[4] # The rplot data structure, it has the setup info. - - vtab = moose.vec( tabname ) - t = np.arange( 0, vtab[0].vector.size, 1 ) * vtab[0].dt - ftype = rp.filename[-4:] - if ftype == '.xml': - self._writeXML( i, t, vtab ) - elif ftype == '.csv': - self._writeCSV( i, t, vtab ) + timeSeriesTable = self._getTimeSeriesTable() + for i,sList in enumerate(self.saveList): + + if (len(sList) >= 6) and (type(sList[5]) != int): + self.saveAs.extend(filter(lambda fmt: type(fmt)!=int, sList[5:])) + try: + timeSeriesData = timeSeriesTable[i] + except IndexError: + print("The object to be plotted has all dummy elements.") + pass + self._saveFormats(timeSeriesData, i, self.saveAs) + self.saveAs=[] else: - print("Save format '{}' not known, please use .csv or .xml".format( ftype ) ) + pass + else: + pass ################################################################ # Here we set up the stims @@ -946,17 +1080,17 @@ rdesigneur.rmoogli.updateMoogliViewer() k = 0 # Stimlist = [path, geomExpr, relPath, field, expr_string] for i in self.stimList: - pair = i.elecpath + " " + i.geom_expr + pair = i[0] + " " + i[1] dendCompts = self.elecid.compartmentsFromExpression[ pair ] spineCompts = self.elecid.spinesFromExpression[ pair ] #print( "pair = {}, numcompts = {},{} ".format( pair, len( dendCompts), len( spineCompts ) ) ) - if i.field == 'vclamp': + if i[3] == 'vclamp': stimObj3 = self._buildVclampOnCompt( dendCompts, spineCompts, i ) stimField = 'commandIn' - elif i.field == 'randsyn': + elif i[3] == 'randsyn': stimObj3 = self._buildSynInputOnCompt( dendCompts, spineCompts, i ) stimField = 'setRate' - elif i.field == 'periodicsyn': + elif i[3] == 'periodicsyn': stimObj3 = self._buildSynInputOnCompt( dendCompts, spineCompts, i, doPeriodic = True ) stimField = 'setRate' else: @@ -969,7 +1103,7 @@ rdesigneur.rmoogli.updateMoogliViewer() funcname = stims.path + '/stim' + str(k) k += 1 func = moose.Function( funcname ) - func.expr = i.expr + func.expr = i[4] #if i[3] == 'vclamp': # Hack to clean up initial condition func.doEvalAtReinit = 1 for q in stimObj3: @@ -1324,7 +1458,7 @@ rdesigneur.rmoogli.updateMoogliViewer() return if not hasattr( self, 'dendCompt' ): raise BuildError( "configureSolvers: no chem meshes defined." ) - fixXreacs( self.chemid.path ) + fixXreacs.fixXreacs( self.chemid.path ) dmksolve = moose.Ksolve( self.dendCompt.path + '/ksolve' ) dmdsolve = moose.Dsolve( self.dendCompt.path + '/dsolve' ) dmstoich = moose.Stoich( self.dendCompt.path + '/stoich' ) @@ -1485,95 +1619,3 @@ rdesigneur.rmoogli.updateMoogliViewer() for j in range( i[1], i[2] ): moose.connect( i[3], 'requestOut', chemVec[j], chemFieldSrc) msg = moose.connect( i[3], 'output', elObj, elecFieldDest ) - - -####################################################################### -# Some helper classes, used to define argument lists. -####################################################################### - -class baseplot: - def __init__( self, - elecpath='soma', geom_expr='1', relpath='.', field='Vm' ): - self.elecpath = elecpath - self.geom_expr = geom_expr - self.relpath = relpath - self.field = field - -class rplot( baseplot ): - def __init__( self, - elecpath = 'soma', geom_expr = '1', relpath = '.', field = 'Vm', - title = 'Membrane potential', - mode = 'time', - ymin = 0.0, ymax = 0.0, - saveFile = "", saveResolution = 3, show = True ): - baseplot.__init__( self, elecpath, geom_expr, relpath, field ) - self.title = title - self.mode = mode # Options: time, wave, wave_still, raster - self.ymin = ymin # If ymin == ymax, it autoscales. - self.ymax = ymax - if len( saveFile ) < 5: - self.saveFile = "" - else: - f = saveFile.split('.') - if len(f) < 2 or ( f[-1] != 'xml' and f[-1] != 'csv' ): - raise BuildError( "rplot: Filetype is '{}', must be of type .xml or .csv.".format( f[-1] ) ) - self.saveFile = saveFile - self.show = show - - def printme( self ): - print( "{}, {}, {}, {}, {}, {}, {}, {}, {}, {}".format( - self.elecpath, - self.geom_expr, self.relpath, self.field, self.title, - self.mode, self.ymin, self.ymax, self.saveFile, self.show ) ) - - @staticmethod - def convertArg( arg ): - if isinstance( arg, rplot ): - return arg - elif isinstance( arg, list ): - return rplot( *arg ) - else: - raise BuildError( "rplot initialization failed" ) - -class rmoog( baseplot ): - def __init__( self, - elecpath = 'soma', geom_expr = '1', relpath = '.', field = 'Vm', - title = 'Membrane potential', - ymin = 0.0, ymax = 0.0, - show = True ): # Could put in other display options. - baseplot.__init__( self, elecpath, geom_expr, relpath, field ) - self.title = title - self.ymin = ymin # If ymin == ymax, it autoscales. - self.ymax = ymax - self.show = show - - @staticmethod - def convertArg( arg ): - if isinstance( arg, rmoog ): - return arg - elif isinstance( arg, list ): - return rmoog( *arg ) - else: - raise BuildError( "rmoog initialization failed" ) - - # Stimlist = [path, geomExpr, relPath, field, expr_string] -class rstim( baseplot ): - def __init__( self, - elecpath = 'soma', geom_expr = '1', relpath = '.', field = 'inject', expr = '0'): - baseplot.__init__( self, elecpath, geom_expr, relpath, field ) - self.expr = expr - - def printme( self ): - print( "{}, {}, {}, {}, {}, {}, {}, {}, {}, {}".format( - self.elecpath, - self.geom_expr, self.relpath, self.field, self.expr ) ) - - @staticmethod - def convertArg( arg ): - if isinstance( arg, rstim ): - return arg - elif isinstance( arg, list ): - return rstim( *arg ) - else: - raise BuildError( "rstim initialization failed" ) - diff --git a/shell/Shell.cpp b/shell/Shell.cpp index 775092c6..e4b29463 100644 --- a/shell/Shell.cpp +++ b/shell/Shell.cpp @@ -348,6 +348,11 @@ void Shell::doStart( double runtime, bool notify ) Streamer* pStreamer = reinterpret_cast<Streamer*>( itr->data( ) ); pStreamer->cleanUp( ); } + + // Print the stats collected by profiling map. + char* p = getenv( "MOOSE_SHOW_SOLVER_PERF" ); + if( p != NULL ) + moose::printSolverProfMap( ); } bool isDoingReinit() diff --git a/shell/Wildcard.cpp b/shell/Wildcard.cpp index 17e69dc2..0ce0567a 100644 --- a/shell/Wildcard.cpp +++ b/shell/Wildcard.cpp @@ -103,18 +103,6 @@ static int innerFind( const string& path, vector< ObjId >& ret) Shell* s = reinterpret_cast< Shell* >( ObjId().data() ); start = s->getCwe(); } - - /* - if ( path[0] == '/' ) { - // separateString puts in a blank first entry if the first char - // is a separator. - separateString( path.substr( 1 ) , names, "/" ); - } else { - Shell* s = reinterpret_cast< Shell* >( Id.eref().data() ); - separateString( path, names, "/" ); - start = s->getCwe(); - } - */ return wildcardRelativeFind( start, names, 0, ret ); } @@ -141,7 +129,6 @@ int simpleWildcardFind( const string& path, vector< ObjId >& ret) unsigned int n = ret.size(); vector< string > wildcards; Shell::chopString( path, wildcards, ',' ); - // separateString( path, wildcards, "," ); vector< string >::iterator i; for ( i = wildcards.begin(); i != wildcards.end(); ++i ) innerFind( *i, ret ); diff --git a/synapse/GraupnerBrunel2012CaPlasticitySynHandler.cpp b/synapse/GraupnerBrunel2012CaPlasticitySynHandler.cpp index 7f78996d..fe8a52a7 100644 --- a/synapse/GraupnerBrunel2012CaPlasticitySynHandler.cpp +++ b/synapse/GraupnerBrunel2012CaPlasticitySynHandler.cpp @@ -269,8 +269,6 @@ void GraupnerBrunel2012CaPlasticitySynHandler::reinitSeed( void ) if( 0 == seed_ ) seed_ = rd_(); - - MOOSE_DEBUG( "Seed is set to " << seed_ ); rng_.seed( seed_ ); } diff --git a/tests/python/fixXreacs.py b/tests/python/fixXreacs.py deleted file mode 100644 index 7a68ed9b..00000000 --- a/tests/python/fixXreacs.py +++ /dev/null @@ -1,194 +0,0 @@ -# -*- coding: utf-8 -*- -#################################################################### -# fixXreacs.py -# The program is meant to take a model and reconfigure any cross-compartment -# reactions so that they are split into portions on either side coupled -# either by diffusion, or by a reaction-driven translocation process. -# -# Copyright (C) Upinder S. Bhalla 2018 -# This program is free software. It is licensed under the terms of -# GPL version 3 or later at your discretion. -# This program carries no warranty whatsoever. -#################################################################### - - -import sys -import moose - -msgSeparator = "_xMsg_" - -def findCompt( elm ): - elm = moose.element( elm ) - pa = elm.parent - while pa.path != '/': - if moose.Neutral(pa).isA[ 'ChemCompt' ]: - return pa.path - pa = pa.parent - print( 'Error: No compartment parent found for ' + elm.path ) - return '/' - -# http://stackoverflow.com/q/3844948/ -def checkEqual(lst): - return not lst or lst.count(lst[0]) == len(lst) - -def findXreacs( basepath, reacType ): - reacs = moose.wildcardFind( basepath + '/##[ISA=' + reacType + 'Base]' ) - ret = [] - for i in reacs: - reacc = findCompt( i ) - subs = i.neighbors['subOut'] - prds = i.neighbors['prdOut'] - subc = [findCompt(j) for j in subs] - prdc = [findCompt(j) for j in prds] - - enzc = [] - if reacType == 'Enz': - enzc = [reacc] - if not checkEqual( subc + prdc + enzc ): - ret.append( [i, reacc, subs, subc, prds, prdc ]) - return ret - -def removeEnzFromPool( pool ): - kids = moose.wildcardFind( pool.path + "/#" ) - for i in kids: - if i.isA[ 'EnzBase' ]: - moose.delete( i ) - elif i.isA[ 'Function' ]: - moose.delete( i ) - -# If a pool is not in the same compt as reac, make a proxy in the reac -# compt, connect it up, and disconnect the one in the old compt. -def proxify( reac, reacc, direction, pool, poolc ): - reacc_elm = moose.element( reacc ) - reac_elm = moose.element( reac ) - # Preserve the rates which were set up for the x-compt reacn - #moose.showfield( reac ) - dupname = pool.name + '_xfer_' + moose.element(poolc).name - #print "#############", pool, dupname, poolc - if moose.exists( reacc + '/' + dupname ): - duppool = moose.element( reacc + '/' + dupname ) - else: - # This also deals with cases where the duppool is buffered. - duppool = moose.copy(pool, reacc_elm, dupname ) - duppool.diffConst = 0 # diffusion only happens in original compt - removeEnzFromPool( duppool ) - disconnectReactant( reac, pool, duppool ) - moose.connect( reac, direction, duppool, 'reac' ) - #moose.showfield( reac ) - #moose.showmsg( reac ) - -def enzProxify( enz, enzc, direction, pool, poolc ): - if enzc == poolc: - return - enze = moose.element( enz ) - # kcat and k2 are indept of volume, just time^-1 - km = enze.numKm - proxify( enz, enzc, direction, pool, poolc ) - enze.numKm = km - -def reacProxify( reac, reacc, direction, pool, poolc ): - if reacc == poolc: - return - reac_elm = moose.element( reac ) - kf = reac_elm.numKf - kb = reac_elm.numKb - proxify( reac, reacc, direction, pool, poolc ) - reac_elm.numKf = kf - reac_elm.numKb = kb - -def identifyMsg( src, srcOut, dest ): - if src.isA[ 'ReacBase' ] or src.isA[ 'EnzBase' ]: - if srcOut == 'subOut': - return msgSeparator + src.path + ' sub ' + dest.path + ' reac' - if srcOut == 'prdOut': - return msgSeparator + src.path + ' prd ' + dest.path + ' reac' - return '' - -def disconnectReactant( reacOrEnz, reactant, duppool ): - outMsgs = reacOrEnz.msgOut - infoPath = duppool.path + '/info' - if moose.exists( infoPath ): - info = moose.element( infoPath ) - else: - info = moose.Annotator( infoPath ) - - #moose.le( reactant ) - notes = "" - #moose.showmsg( reacOrEnz ) - for i in outMsgs: - #print "killing msg from {} to {}\nfor {} and {}".format( reacOrEnz.path, reactant.path, i.srcFieldsOnE1[0], i.srcFieldsOnE2[0] ) - if i.e1 == reactant: - msgStr = identifyMsg( i.e2, i.e2.srcFieldsOnE2[0], i.e1 ) - if len( msgStr ) > 0: - notes += msgStr - moose.delete( i ) - elif i.e2 == reactant: - msgStr = identifyMsg( i.e1[0], i.srcFieldsOnE1[0], i.e2[0] ) - if len( msgStr ) > 0: - notes += msgStr - moose.delete( i ) - #print "MSGS to rebuild:", notes - info.notes += notes - -def fixXreacs( basepath ): - xr = findXreacs( basepath, 'Reac' ) - xe = findXreacs( basepath, 'Enz' ) - - for i in (xr): - reac, reacc, subs, subc, prds, prdc = i - for j in range( len( subs )): - reacProxify( reac, reacc, 'sub', subs[j], subc[j] ) - for j in range( len( prds )): - reacProxify( reac, reacc, 'prd', prds[j], prdc[j] ) - - for i in (xe): - reac, reacc, subs, subc, prds, prdc = i - for j in range( len( subs )): - enzProxify( reac, reacc, 'sub', subs[j], subc[j] ) - for j in range( len( prds )): - enzProxify( reac, reacc, 'prd', prds[j], prdc[j] ) - -##################################################################### - -def getOldRates( msgs ): - if len( msgs ) > 1 : - m1 = msgs[1].split( msgSeparator )[0] - elm = moose.element( m1.split( ' ' )[0] ) - if elm.isA[ 'ReacBase' ]: - return [elm.numKf, elm.numKb] - elif elm.isA[ 'EnzBase' ]: - return [elm.numKm,] - print( "Warning: getOldRates did not have any messages" ) - return [0,] - -def restoreOldRates( oldRates, msgs ): - #print oldRates, msgs - if len( msgs ) > 1 : - m1 = msgs[1].split( msgSeparator )[0] - elm = moose.element( m1.split( ' ' )[0] ) - if elm.isA[ 'ReacBase' ]: - elm.numKf = oldRates[0] - elm.numKb = oldRates[1] - elif elm.isA[ 'enzBase' ]: - elm.numKm = oldRates[0] - - - -def restoreXreacs( basepath ): - proxyInfo = moose.wildcardFind( basepath + "/##/#_xfer_#/info" ) - for i in proxyInfo: - msgs = i.notes.split( msgSeparator ) - oldRates = getOldRates( msgs ) - #print( "Deleting {}".format( i.parent.path ) ) - #print msgs - moose.delete( i.parent ) - for j in msgs: - if len( j ) > 0: - args = j.split( ' ' ) - assert( len( args ) == 4 ) - #moose.showfield( args[0] ) - moose.connect( args[0], args[1], args[2], args[3] ) - #print( "Reconnecting {}".format( args ) ) - #moose.showfield( args[0] ) - restoreOldRates( oldRates, msgs ) - diff --git a/tests/python/testXchan1.py b/tests/python/testXchan1.py index 1bcc130b..c62f1427 100644 --- a/tests/python/testXchan1.py +++ b/tests/python/testXchan1.py @@ -31,7 +31,7 @@ import matplotlib.pyplot as plt import matplotlib.image as mpimg import moose print( '[INFO ] Using moose from %s, %s' % (moose.__file__, moose.version()) ) -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/testXdiff1.py b/tests/python/testXdiff1.py index bbf73e87..71b30993 100644 --- a/tests/python/testXdiff1.py +++ b/tests/python/testXdiff1.py @@ -30,7 +30,7 @@ import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import moose -import fixXreacs +import moose.fixXreacs as fixXreacs diffConst = 1e-16 diff --git a/tests/python/testXenz1.py b/tests/python/testXenz1.py index b359d44e..80071aa5 100644 --- a/tests/python/testXenz1.py +++ b/tests/python/testXenz1.py @@ -20,7 +20,7 @@ import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import moose -import fixXreacs +import moose.fixXreacs as fixXreacs subInit = 0.002 eInit = 0.001 diff --git a/tests/python/testXreacs2.py b/tests/python/testXreacs2.py index 289c2b24..1d5bed33 100644 --- a/tests/python/testXreacs2.py +++ b/tests/python/testXreacs2.py @@ -5,7 +5,7 @@ import matplotlib.pyplot as plt mpl.rcParams['text.usetex'] = False import moose import numpy as np -import fixXreacs +import moose.fixXreacs as fixXreacs def countCrossings( plot, thresh ): vec = moose.element( plot ).vector diff --git a/tests/python/testXreacs3.py b/tests/python/testXreacs3.py index 4e5ef170..fa99656f 100644 --- a/tests/python/testXreacs3.py +++ b/tests/python/testXreacs3.py @@ -25,7 +25,7 @@ import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/testXreacs4.py b/tests/python/testXreacs4.py index 4fd44007..d618e05c 100644 --- a/tests/python/testXreacs4.py +++ b/tests/python/testXreacs4.py @@ -14,7 +14,7 @@ import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/testXreacs4a.py b/tests/python/testXreacs4a.py index 2fe009e9..482c0dba 100644 --- a/tests/python/testXreacs4a.py +++ b/tests/python/testXreacs4a.py @@ -14,7 +14,7 @@ import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/testXreacs5.py b/tests/python/testXreacs5.py index e46bc459..7012dd96 100644 --- a/tests/python/testXreacs5.py +++ b/tests/python/testXreacs5.py @@ -14,7 +14,7 @@ import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/testXreacs5a.py b/tests/python/testXreacs5a.py index 330419ea..3d552f34 100644 --- a/tests/python/testXreacs5a.py +++ b/tests/python/testXreacs5a.py @@ -14,7 +14,7 @@ import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/testXreacs6.py b/tests/python/testXreacs6.py index 7edb514d..75bd905b 100644 --- a/tests/python/testXreacs6.py +++ b/tests/python/testXreacs6.py @@ -14,7 +14,7 @@ import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/testXreacs7.py b/tests/python/testXreacs7.py index b55de268..73008321 100644 --- a/tests/python/testXreacs7.py +++ b/tests/python/testXreacs7.py @@ -16,7 +16,7 @@ import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/testXreacs8.py b/tests/python/testXreacs8.py index 58dc5cf4..0ae0dba9 100644 --- a/tests/python/testXreacs8.py +++ b/tests/python/testXreacs8.py @@ -25,7 +25,7 @@ import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/test_Xchan1.py b/tests/python/test_Xchan1.py index 35fc56f6..af555663 100644 --- a/tests/python/test_Xchan1.py +++ b/tests/python/test_Xchan1.py @@ -28,7 +28,7 @@ import math import numpy as np import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/test_Xdiff1.py b/tests/python/test_Xdiff1.py index fc9df5d8..6a7545c9 100644 --- a/tests/python/test_Xdiff1.py +++ b/tests/python/test_Xdiff1.py @@ -29,7 +29,7 @@ import math import numpy as np import moose print( '[INFO] Using moose from %s, %s' % (moose.__file__, moose.version()) ) -import fixXreacs +import moose.fixXreacs as fixXreacs diffConst = 1e-16 diff --git a/tests/python/test_Xenz1.py b/tests/python/test_Xenz1.py index c7463ad4..13b7d4b8 100644 --- a/tests/python/test_Xenz1.py +++ b/tests/python/test_Xenz1.py @@ -18,7 +18,7 @@ import math import numpy as np import moose -import fixXreacs +import moose.fixXreacs as fixXreacs subInit = 0.002 eInit = 0.001 diff --git a/tests/python/test_Xreacs2.py b/tests/python/test_Xreacs2.py index 1b1e7302..4089501c 100644 --- a/tests/python/test_Xreacs2.py +++ b/tests/python/test_Xreacs2.py @@ -3,7 +3,7 @@ import os import sys import moose import numpy as np -import fixXreacs +import moose.fixXreacs as fixXreacs def countCrossings( plot, thresh ): vec = moose.element( plot ).vector diff --git a/tests/python/test_Xreacs3.py b/tests/python/test_Xreacs3.py index 53cd7600..889e0b1c 100644 --- a/tests/python/test_Xreacs3.py +++ b/tests/python/test_Xreacs3.py @@ -23,7 +23,7 @@ import math import numpy as np import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/test_Xreacs4.py b/tests/python/test_Xreacs4.py index c655e4e5..d96497b9 100644 --- a/tests/python/test_Xreacs4.py +++ b/tests/python/test_Xreacs4.py @@ -12,7 +12,7 @@ import math import numpy as np import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/test_Xreacs4a.py b/tests/python/test_Xreacs4a.py index 73c6d547..ffad55b3 100644 --- a/tests/python/test_Xreacs4a.py +++ b/tests/python/test_Xreacs4a.py @@ -12,7 +12,7 @@ import math import numpy as np import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/test_Xreacs5.py b/tests/python/test_Xreacs5.py index 4dde2a47..60acd3cf 100644 --- a/tests/python/test_Xreacs5.py +++ b/tests/python/test_Xreacs5.py @@ -12,7 +12,7 @@ import math import numpy as np import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model @@ -91,8 +91,10 @@ def main( standalone = False ): moose.reinit() moose.start( runtime ) - assert( almostEq( 2.0 * moose.element( 'model/compartment/s' ).conc, - moose.element( '/model/endo/s' ).conc ) ) + e1 = moose.element( 'model/compartment/s' ) + e2 = moose.element( 'model/endo/s' ) + assert almostEq(2.0 * e1.conc, e2.conc), \ + "Expected %g, Got %g" % (e1.conc, e2.conc ) moose.delete( '/model' ) diff --git a/tests/python/test_Xreacs5a.py b/tests/python/test_Xreacs5a.py index 126f9182..09a2c17e 100644 --- a/tests/python/test_Xreacs5a.py +++ b/tests/python/test_Xreacs5a.py @@ -12,7 +12,7 @@ import math import numpy as np import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/test_Xreacs6.py b/tests/python/test_Xreacs6.py index 35f21c5b..06695267 100644 --- a/tests/python/test_Xreacs6.py +++ b/tests/python/test_Xreacs6.py @@ -12,7 +12,7 @@ import math import numpy as np import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/test_Xreacs7.py b/tests/python/test_Xreacs7.py index 913a7a1c..5df168bc 100644 --- a/tests/python/test_Xreacs7.py +++ b/tests/python/test_Xreacs7.py @@ -14,7 +14,7 @@ import math import numpy as np import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/test_Xreacs8.py b/tests/python/test_Xreacs8.py index 15bb0f01..d2ef6de3 100644 --- a/tests/python/test_Xreacs8.py +++ b/tests/python/test_Xreacs8.py @@ -23,7 +23,7 @@ import math import numpy as np import moose -import fixXreacs +import moose.fixXreacs as fixXreacs def makeModel(): # create container for model diff --git a/tests/python/test_cylinder_diffusion_gsolve+dsolve.py b/tests/python/test_cylinder_diffusion_gsolve+dsolve.py new file mode 100644 index 00000000..db3dd773 --- /dev/null +++ b/tests/python/test_cylinder_diffusion_gsolve+dsolve.py @@ -0,0 +1,146 @@ +######################################################################### +## This program is part of 'MOOSE', the +## Messaging Object Oriented Simulation Environment. +## Copyright (C) 2014 Upinder S. Bhalla. and NCBS +## It is made available under the terms of the +## GNU Lesser General Public License version 2.1 +## See the file COPYING.LIB for the full notice. +######################################################################### + + +import sys +import math +import numpy as np +import moose +print('[INFO] Using moose from %s' % moose.__file__ ) +import os + +moose.seed( 1 ) + +concA = 0.005 # millimolar +def makeModel(): + # create container for model + r0 = 2e-6 # m + r1 = 1e-6 # m + num = 100 + diffLength = 1e-6 # m + L = num * diffLength # m + diffConst = 10e-12 + #motorRate = 1e-6 + #diffConst = 0 + motorRate = 0 + + model = moose.Neutral( 'model' ) + compartment = moose.CylMesh( '/model/compartment' ) + compartment.r0 = r0 + compartment.r1 = r1 + compartment.x0 = 0 + compartment.x1 = L + compartment.diffLength = diffLength + assert( compartment.numDiffCompts == num ) + + # create molecules and reactions + a = moose.Pool( '/model/compartment/a' ) + b = moose.Pool( '/model/compartment/b' ) + c = moose.Pool( '/model/compartment/c' ) + d = moose.Pool( '/model/compartment/d' ) + r1 = moose.Reac( '/model/compartment/r1' ) + moose.connect( r1, 'sub', b, 'reac' ) + moose.connect( r1, 'sub', d, 'reac' ) + moose.connect( r1, 'prd', c, 'reac' ) + r1.Kf = 1000.0 # 1/(mM.sec) + r1.Kb = 1 # 1/sec + + # Assign parameters + a.diffConst = diffConst + b.diffConst = diffConst / 2.0 + b.motorConst = motorRate + c.diffConst = diffConst + d.diffConst = diffConst + + # Make solvers + ksolve = moose.Gsolve( '/model/compartment/ksolve' ) + dsolve = moose.Dsolve( '/model/compartment/dsolve' ) + stoich = moose.Stoich( '/model/compartment/stoich' ) + stoich.compartment = compartment + stoich.ksolve = ksolve + stoich.dsolve = dsolve + stoich.path = "/model/compartment/##" + assert( dsolve.numPools == 4 ) + a.vec.concInit = concA + b.vec.concInit = concA / 5.0 + c.vec.concInit = concA + d.vec.concInit = concA / 5.0 + for i in range( num ): + d.vec[i].concInit = concA * 2 * i / num + +def main(): + """ + This example illustrates how to set up a diffusion/transport model with + a simple reaction-diffusion system in a tapering cylinder: + + | Molecule **a** diffuses with diffConst of 10e-12 m^2/s. + | Molecule **b** diffuses with diffConst of 5e-12 m^2/s. + | Molecule **b** also undergoes motor transport with a rate of 10e-6 m/s + | Thus it 'piles up' at the end of the cylinder. + | Molecule **c** does not move: diffConst = 0.0 + | Molecule **d** does not move: diffConst = 10.0e-12 but it is buffered. + | Because it is buffered, it is treated as non-diffusing. + + All molecules other than **d** start out only in the leftmost (first) + voxel, with a concentration of 1 mM. **d** is present throughout + at 0.2 mM, except in the last voxel, where it is at 1.0 mM. + + The cylinder has a starting radius of 2 microns, and end radius of + 1 micron. So when the molecule undergoing motor transport gets to the + narrower end, its concentration goes up. + + There is a little reaction in all compartments: ``b + d <===> c`` + + As there is a high concentration of **d** in the last compartment, + when the molecule **b** reaches the end of the cylinder, the reaction + produces lots of **c**. + + Note that molecule **a** does not participate in this reaction. + + The concentrations of all molecules are displayed in an animation. + """ + runtime = 20.0 + diffdt = 0.005 + plotdt = 0.1 + makeModel() + # Set up clocks. The dsolver to know before assigning stoich + moose.setClock( 10, diffdt ) # 10 is the standard clock for Dsolve. + moose.setClock( 16, plotdt ) # 16 is the standard clock for Ksolve. + + a = moose.element( '/model/compartment/a' ) + b = moose.element( '/model/compartment/b' ) + c = moose.element( '/model/compartment/c' ) + d = moose.element( '/model/compartment/d' ) + + moose.reinit() + atot = sum( a.vec.n ) + btot = sum( b.vec.n ) + ctot = sum( c.vec.n ) + dtot = sum( d.vec.n ) + for t in np.arange( 0, runtime, plotdt ): + moose.start( plotdt ) + + atot2 = sum( a.vec.n ) + btot2 = sum( b.vec.n ) + ctot2 = sum( c.vec.n ) + dtot2 = sum( d.vec.n ) + + msg = 'Ratio of initial to final total numbers of ' + got = np.array((atot2/atot, btot2/btot, ctot2/ctot, dtot2/dtot)) + msg += 'a=%f b=%f, c=%f, d=%f' % (tuple(got)) + print(msg) + print('Initial to final (b+c)=%f' % (float(btot2 + ctot2) / (btot + ctot ))) + expected = np.array((1.00003087, 1.39036644, 0.92191184, 1.11427514)) + error = got - expected + rerror = np.abs( error ) / expected + assert np.allclose(got, expected, atol=1e-3), "Got %s, expected %s" % (got, expected) + +# Run the 'main' if this script is executed standalone. +if __name__ == '__main__': + main() diff --git a/tests/python/test_gsolve_parallel.py b/tests/python/test_gsolve_parallel.py new file mode 100644 index 00000000..e7887ce8 --- /dev/null +++ b/tests/python/test_gsolve_parallel.py @@ -0,0 +1,83 @@ +import numpy as np +import moose +print( '[INFO] Using moose from %s' % moose.__file__ ) +import time +moose.seed( 10 ) + +def main( nT ): + """ + This example implements a reaction-diffusion like system which is + bistable and propagates losslessly. It is based on the NEURON example + rxdrun.py, but incorporates more compartments and runs for a longer time. + The system is implemented in a function rather than as a proper system + of chemical reactions. Please see rxdReacDiffusion.py for a variant that + uses a reaction plus a function object to control its rates. + """ + print( 'Using %d threads' % nT ) + + dt = 0.1 + + # define the geometry + compt = moose.CylMesh( '/cylinder' ) + compt.r0 = compt.r1 = 100e-9 + compt.x1 = 200e-9 + compt.diffLength = 0.2e-9 + assert( compt.numDiffCompts == compt.x1/compt.diffLength ) + + #define the molecule. Its geometry is defined by its parent volume, cylinder + c = moose.Pool( '/cylinder/pool' ) + c.diffConst = 1e-13 # define diffusion constant + + + # Here we set up a function calculation + func = moose.Function( '/cylinder/pool/func' ) + func.expr = "(-x0 * (30e-9 - x0) * (100e-9 - x0))*0.0001" + func.x.num = 1 #specify number of input variables. + + #Connect the molecules to the func + moose.connect( c, 'nOut', func.x[0], 'input' ) + #Connect the function to the pool + moose.connect( func, 'valueOut', c, 'increment' ) + + #Set up solvers + ksolve = moose.Gsolve( '/cylinder/Gsolve' ) + try: + ksolve.numThreads = nT + except Exception as e: + print( 'OLD MOOSE. Does not support multithreading' ) + dsolve = moose.Dsolve( '/cylinder/dsolve' ) + stoich = moose.Stoich( '/cylinder/stoich' ) + stoich.compartment = compt + stoich.ksolve = ksolve + stoich.dsolve = dsolve + stoich.path = '/cylinder/##' + + #initialize + x = np.arange( 0, compt.x1, compt.diffLength ) + c.vec.nInit = [ 1000.0 for q in x ] + + # Run and plot it. + moose.reinit() + updateDt = 50 + runtime = updateDt * 10 + t1 = time.time() + res = [] + for t in range( 0, runtime-1, updateDt ): + moose.start( updateDt ) + y = c.vec.n + res.append( (np.mean(y), np.std(y)) ) + + expected = [(9.0, 0.0), (6.0, 0.0), (5.0, 0.0), (3.0, 0.0), (2.0, 0.0), + (2.0, 0.0), (2.0, 0.0), (1.0, 0.0), (1.0, 0.0), (1.0, 0.0)] + print(("Time = ", time.time() - t1)) + print( res ) + assert res == expected + +if __name__ == '__main__': + import sys + import multiprocessing + nT = int(multiprocessing.cpu_count() / 2) + if len(sys.argv) > 1: + nT = int(sys.argv[1]) + main( nT ) + diff --git a/tests/python/test_ksolve.py b/tests/python/test_ksolve.py index f70d889d..4edabc93 100644 --- a/tests/python/test_ksolve.py +++ b/tests/python/test_ksolve.py @@ -32,6 +32,7 @@ ksolve = moose.Ksolve( '/compt/ksolve' ) stoich = moose.Stoich( '/compt/stoich' ) stoich.compartment = compt stoich.ksolve = ksolve +ksolve.numThreads = 2 stoich.path = '/compt/##' moose.reinit() print( '[INFO] Using method = %s' % ksolve.method ) diff --git a/tests/python/test_ksolve_parallel.py b/tests/python/test_ksolve_parallel.py new file mode 100644 index 00000000..72da77d7 --- /dev/null +++ b/tests/python/test_ksolve_parallel.py @@ -0,0 +1,105 @@ +import os +import numpy as np +import moose +print( 'Using moose from %s' % moose.__file__ ) +import time +os.environ['MOOSE_SHOW_PROFILING_INFO'] = '1' + +def main( nthreads = 1 ): + """ + This example implements a reaction-diffusion like system which is + bistable and propagates losslessly. It is based on the NEURON example + rxdrun.py, but incorporates more compartments and runs for a longer time. + The system is implemented as a hybrid of a reaction and a function which + sets its rates. Please see rxdFuncDiffusion.py for a variant that uses + just a function object to set up the system. + """ + + dt = 0.1 + + # define the geometry + compt = moose.CylMesh( '/cylinder' ) + compt.r0 = compt.r1 = 1 + # compt.diffLength = 1e-6 + compt.x1 = 1e-2 + assert compt.numDiffCompts == int(compt.x1/compt.diffLength), ( + compt.numDiffCompts, compt.x1 / compt.diffLength ) + print( 'No of compartment %d' % compt.numDiffCompts ) + + #define the molecule. Its geometry is defined by its parent volume, cylinder + c = moose.Pool( '/cylinder/pool' ) + c.diffConst = 1 # define diffusion constant + # There is an implicit reaction substrate/product. MOOSE makes it explicit. + buf = moose.BufPool( '/cylinder/buf' ) + buf.nInit = 1 + + # The reaction is something entirely peculiar, not a chemical thing. + reaction = moose.Reac( '/cylinder/reac' ) + reaction.Kb = 0 + + # so here we set up a function calculation to do the same thing. + func = moose.Function( '/cylinder/reac/func' ) + func.expr = "(1 - x0) * (0.3 - x0)" + func.x.num = 1 #specify number of input variables. + + #Connect the reaction to the pools + moose.connect( reaction, 'sub', c, 'reac' ) + moose.connect( reaction, 'prd', buf, 'reac' ) + + #Connect the function to the reaction + moose.connect( func, 'valueOut', reaction, 'setNumKf' ) + + #Connect the molecules to the func + moose.connect( c, 'nOut', func.x[0], 'input' ) + + #Set up solvers + ksolve = moose.Ksolve( '/cylinder/ksolve' ) + ksolve.numThreads = nthreads + dsolve = moose.Dsolve( '/cylinder/dsolve' ) + stoich = moose.Stoich( '/cylinder/stoich' ) + stoich.compartment = compt + stoich.ksolve = ksolve + stoich.dsolve = dsolve + stoich.path = '/cylinder/##' + for i in range( 10, 18 ): + moose.setClock( i, dt ) + + #initialize + x = np.arange( 0, compt.x1, compt.diffLength ) + c.vec.nInit = [ (q < 0.2 * compt.x1) for q in x ] + + expected = [ (0.2, 0.40000000000000013) + , (2.6704795776286974e-07, 1.2678976830753021e-17) + , (8.167639617309419e-14, 3.8777269301457245e-24) + , (2.498062905267963e-20, 1.1860363878961374e-30) + , (7.64029581501609e-27, 3.6273808003690943e-37) + ] + + # Run and plot it. + moose.reinit() + updateDt = 50 + runtime = updateDt * 4 + # plt = pylab.plot( x, c.vec.n, label='t = 0 ') + yvec = c.vec.n + u1, m1 = np.mean( yvec ), np.std( yvec ) + print( u1, m1 ) + assert np.isclose( (u1, m1), expected[0], atol=1e-5).all(), expected[0] + t1 = time.time() + for i, t in enumerate(range( 0, runtime-1, updateDt)): + moose.start( updateDt ) + # plt = pylab.plot( x, c.vec.n, label='t = '+str(t + updateDt) ) + yvec = c.vec.n + u1, m1 = np.mean( yvec ), np.std( yvec ) + print( u1, m1 ) + np.isclose( (u1,m1), expected[i+1], atol=1e-5 ).all(), expected[i+1] + return time.time() - t1 + + +if __name__ == '__main__': + import multiprocessing + import sys + nT = int(multiprocessing.cpu_count()/2) + if len(sys.argv) > 1: + nT = int(sys.argv[1]) + t2 = main( nT ) + print( 'With threads=%d, Time=%g' % (nT, t2)) diff --git a/utility/print_function.hpp b/utility/print_function.hpp index d3a9e888..4b984d7c 100644 --- a/utility/print_function.hpp +++ b/utility/print_function.hpp @@ -59,7 +59,7 @@ using namespace std; */ /* ----------------------------------------------------------------------------*/ #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) -#ifdef NDEBUG +#ifndef NDEBUG #define MOOSE_DEBUG( a ) { \ stringstream ss; ss << a; \ cout << "DEBUG: " << __FILENAME__ << ":" << __LINE__ << "| " << ss.str(); \ -- GitLab