diff --git a/.travis.yml b/.travis.yml index 294e0896243791f983832cc2272705c819a76702..b5f3f688e66c00d97989013b897183b478ca3c70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,9 +34,7 @@ script: - if type python3 > /dev/null; then python3 -m compileall -q . ; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ./.travis/travis_build_osx.sh; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./.travis/travis_build_linux.sh; fi -- set +e -deploy: - on: tags - provider: script - script: ./.travis/deploy_pypi.sh +addons: + apt: + update: true diff --git a/.travis/travis_build_linux.sh b/.travis/travis_build_linux.sh index e475b2e0bf622b47ac46a08f0b14eaa308c08b03..3009af21b0f185f8dbe2760711cc4b5663ec6f5f 100755 --- a/.travis/travis_build_linux.sh +++ b/.travis/travis_build_linux.sh @@ -45,6 +45,8 @@ echo "Currently in `pwd`" mkdir -p _GSL_BUILD && cd _GSL_BUILD cmake -DDEBUG=ON -DPYTHON_EXECUTABLE="$PYTHON2" .. $MAKE && ctest --output-on-failure + sudo make install + $PYTHON2 -c 'import moose;print(moose.__file__);print(moose.version())' ) ( diff --git a/CMakeLists.txt b/CMakeLists.txt index 349d580c9a7357cc4c243538a8e2ee228a66006c..bd6aaf84449898d713efba60273018fa55b0bf0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -401,7 +401,7 @@ set(EXTRA_ARGS "--prefix ${CMAKE_INSTALL_PREFIX} ${DISTUTILS_EXTRA_ARGS}") # On Debian/Ubuntu install using debian layout. # NOTE: Also create setup.cfg file which setup prefix and install-layout # suitable for DEBIAN systems. -if(${_platform_desc} MATCHES ".*(Ubuntu|Debian).*") +if(${_platform_desc} MATCHES ".*(Ubuntu|debian).*") list(APPEND EXTRA_ARGS "--install-layout=deb") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/python/setup.cfg "[install]\nprefix=/usr\ninstall-layout=deb" @@ -415,7 +415,11 @@ find_package(PythonInterp REQUIRED) if(NOT PYMOOSE_BDIST_DIR) set(PYMOOSE_BDIST_DIR ${CMAKE_BINARY_DIR}/bdist) endif( ) -set(PYMOOSE_BDIST_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/pymoose_bdist_install) + +# If no one hsa set the PYMOOSE bdist installation directory, use default. +if(NOT PYMOOSE_BDIST_INSTALL_DIR) + set(PYMOOSE_BDIST_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/pymoose_bdist_install) +endif() file(MAKE_DIRECTORY ${PYMOOSE_BDIST_INSTALL_DIR}) # We need a custom name for bdist; same on all platform. @@ -426,14 +430,14 @@ add_custom_target(bdist ALL DEPENDS ${PYMOOSE_BDIST_FILE} ) # Any command using setup.cmake.py must run in the same directory. add_custom_command( OUTPUT ${PYMOOSE_BDIST_FILE} - COMMAND ${PYTHON_EXECUTABLE} setup.cmake.py bdist_dumb -p ${_platform} + COMMAND ${PYTHON_EXECUTABLE} setup.cmake.py bdist_dumb -p ${_platform} -d ${PYMOOSE_BDIST_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/python COMMENT "bdist is saved to ${PYMOOSE_BDIST_DIR}" VERBATIM ) add_custom_command(TARGET bdist POST_BUILD - COMMAND ${CMAKE_COMMAND} -E chdir ${PYMOOSE_BDIST_INSTALL_DIR} tar xvf ${PYMOOSE_BDIST_FILE} + COMMAND ${CMAKE_COMMAND} -E chdir ${PYMOOSE_BDIST_INSTALL_DIR} tar xf ${PYMOOSE_BDIST_FILE} COMMENT "Unarchiving bdist file ${PYMOOSE_BDIST_FILE} to ${PYMOOSE_BDIST_INSTALL_DIR}" VERBATIM ) @@ -445,7 +449,7 @@ add_custom_target(copy_pymoose DEPENDS ${PYTHON_SRCS} COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/python ${CMAKE_CURRENT_BINARY_DIR}/python COMMENT "Copying required python files and other files to build directory" - VERBATIM + VERBATIM ) add_dependencies( bdist copy_pymoose ) @@ -455,14 +459,11 @@ add_dependencies( copy_pymoose _moose ) install(TARGETS moose.bin DESTINATION bin CONFIGURATIONS Debug) install(TARGETS libmoose DESTINATION lib CONFIGURATIONS Debug) -install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/scripts/moose - DESTINATION bin CONFIGURATIONS Debug - ) -# install pymoose bdist. -install(DIRECTORY ${PYMOOSE_BDIST_INSTALL_DIR}/ +# install pymoose bdist. The bdist comes with predefined /usr; remove it. +install(DIRECTORY ${PYMOOSE_BDIST_INSTALL_DIR}/usr/ DESTINATION ${CMAKE_INSTALL_PREFIX} - CONFIGURATIONS Release + CONFIGURATIONS Release Debug ) # Print message to start build process diff --git a/biophysics/VClamp.cpp b/biophysics/VClamp.cpp index 9ed2be8632130943b4c1d91a366ea6ed77e2dfe4..b28218d5261621b28f143addbc0ddbee50728389 100644 --- a/biophysics/VClamp.cpp +++ b/biophysics/VClamp.cpp @@ -82,8 +82,9 @@ const Cinfo * VClamp::initCinfo() "Shared message to receive Process messages from the scheduler", processShared, sizeof(processShared) / sizeof(Finfo*)); - static ReadOnlyValueFinfo< VClamp, double> command("command", + static ValueFinfo< VClamp, double> command("command", "Command input received by the clamp circuit.", + &VClamp::setCommand, &VClamp::getCommand); static ValueFinfo< VClamp, unsigned int> mode("mode", "Working mode of the PID controller.\n" @@ -210,14 +211,12 @@ VClamp::~VClamp() void VClamp::setCommand(double value) { - // e2_ = 0; - // e1_ = 0; cmdIn_ = value; } double VClamp::getCommand() const { - return command_; + return cmdIn_; } void VClamp::setTi(double value) @@ -334,13 +333,16 @@ void VClamp::reinit(const Eref& e, ProcPtr p) tauByDt_ = tau_ / p->dt; dtByTi_ = p->dt/ti_; tdByDt_ = td_ / p->dt; - if (Kp_ == 0){ - vector<Id> compartments; - unsigned int numComp = e.element()->getNeighbors(compartments, currentOut()); - if (numComp > 0){ - double Cm = Field<double>::get(compartments[0], "Cm"); + + vector<Id> compartments; + unsigned int numComp = e.element()->getNeighbors(compartments, currentOut()); + if (numComp > 0){ + double Cm = Field<double>::get(compartments[0], "Cm"); + if (Kp_ == 0){ Kp_ = Cm / p->dt; - } + } + command_ = cmdIn_ = oldCmdIn_ = + Field<double>::get(compartments[0], "initVm"); } } diff --git a/device/DiffAmp.cpp b/device/DiffAmp.cpp index 60bbb422fdf41448ff274596f27d1e58734d8bb8..7f7059a41e492d5daae67cdf334ba4c71bac9a70 100644 --- a/device/DiffAmp.cpp +++ b/device/DiffAmp.cpp @@ -206,6 +206,7 @@ void DiffAmp::reinit(const Eref& e, ProcPtr p) output_ = 0.0; plus_ = 0.0; minus_ = 0.0; + outputOut()->send(e, output_); } // diff --git a/device/RC.cpp b/device/RC.cpp index 6ce69f23a741d469e316a31da7cb476034c5bdec..b8b07f36101cec335e171759afe8c88beda34186 100644 --- a/device/RC.cpp +++ b/device/RC.cpp @@ -129,7 +129,7 @@ RC::RC(): state_(0), inject_(0), msg_inject_(0.0), - exp_(0.0), + expTau_(0.0), dt_tau_(0.0) { ; // Do nothing @@ -203,13 +203,30 @@ void RC::setInjectMsg( double inject ) void RC::process(const Eref& e, const ProcPtr proc ) { + /* double sum_inject_prev = inject_ + msg_inject_; double sum_inject = inject_ + msg_inject_; double dVin = (sum_inject - sum_inject_prev) * resistance_; double Vin = sum_inject * resistance_; state_ = Vin + dVin - dVin / dt_tau_ + - (state_ - Vin + dVin / dt_tau_) * exp_; + (state_ - Vin + dVin / dt_tau_) * expTau_; sum_inject_prev = sum_inject; + msg_inject_ = 0.0; + outputOut()->send(e, state_); + */ + + //////////////////////////////////////////////////////////////// + // double A = inject + msgInject_; + // double B = 1.0/resistance_; + // double x = exp( -B * proc->dt / capacitance_ ); + // state_ = state_ * x + (A/B) * (1.0-x); + // double x = exp( -dt_tau_ ); + state_ = state_ * expTau_ + + resistance_*(inject_ + msg_inject_) * (1.0-expTau_); + + /// OR, if we use simple Euler: /// + // state_ += (inject_ + msgInject_ - state_/resistance_ ) * proc->dt / capacitance_; + msg_inject_ = 0.0; outputOut()->send(e, state_); } @@ -220,9 +237,9 @@ void RC::reinit(const Eref& e, const ProcPtr proc) dt_tau_ = proc->dt / (resistance_ * capacitance_); state_ = v0_; if (dt_tau_ > 1e-15){ - exp_ = exp(-dt_tau_); + expTau_ = exp(-dt_tau_); } else {// use approximation - exp_ = 1 - dt_tau_; + expTau_ = 1 - dt_tau_; } msg_inject_ = 0.0; outputOut()->send(e, state_); diff --git a/device/RC.h b/device/RC.h index 3023ddbb1324e26277463a7b71e11e16c39b87d1..abca9dbfcc54b67cbb5948aa61826ce2e3b503fb 100644 --- a/device/RC.h +++ b/device/RC.h @@ -66,7 +66,7 @@ class RC{ double state_; double inject_; double msg_inject_; - double exp_; + double expTau_; double dt_tau_; }; diff --git a/diffusion/ConcChanInfo.h b/diffusion/ConcChanInfo.h new file mode 100644 index 0000000000000000000000000000000000000000..cd16266b5e5ea895ce7a2fec5722bb18de339a50 --- /dev/null +++ b/diffusion/ConcChanInfo.h @@ -0,0 +1,31 @@ +/********************************************************************** +** This program is part of 'MOOSE', the +** Messaging Object Oriented Simulation Environment. +** Copyright (C) 2003-2012 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. +**********************************************************************/ + +#ifndef _CONC_CHAN_INFO_H +#define _CONC_CHAN_INFO_H + +/** + */ +class ConcChanInfo +{ + public: + ConcChanInfo(); + ConcChanInfo( unsigned int my, unsigned int other, + unsigned int chan, double perm ) + : myPool( my ), otherPool( other ), chanPool( chan ), + permeability( perm ) + {;} + + unsigned int myPool; + unsigned int otherPool; + unsigned int chanPool; + double permeability; +}; + +#endif // _CONC_CHAN_INFO_H diff --git a/diffusion/DiffJunction.h b/diffusion/DiffJunction.h index c2ac3070ef8fc4de657554604afbc91d77f343c4..a4e44d23c88d6e9898a38881cdba23af434784af 100644 --- a/diffusion/DiffJunction.h +++ b/diffusion/DiffJunction.h @@ -15,6 +15,17 @@ * * The data includes all the pools that diffuse, which are identified * by their indices in the respective Dsolves. + * + * It includes the pools that are transferred directly, due to cross- + * compartment reactions + * + * It includes the pools that go down a conc gradient due to a + * channel, and also specifies the channel. Equation is the same as for + * diffusion, but scaled by the channel conductance and amount. Channel + * may also do some local calculations for Nernst potentials if applicable. + * Note that this will be flawed unless current is somehow coupled to memb + * potential. + * * It also includes the vector of VoxelJunctions, which specifies * matching voxel indices in the two compartments, and the diffusion scale * factor (based on geometry) for this junction. @@ -25,5 +36,15 @@ class DiffJunction unsigned int otherDsolve; vector< unsigned int > myPools; vector< unsigned int > otherPools; + + vector< unsigned int > myXferSrc; + vector< unsigned int > otherXferDest; + + vector< unsigned int > myXferDest; + vector< unsigned int > otherXferSrc; + + vector< unsigned int > myChannels; + vector< unsigned int > otherChannels; + vector< VoxelJunction > vj; }; diff --git a/diffusion/DiffPoolVec.cpp b/diffusion/DiffPoolVec.cpp index e6faaa63133205c2dc3ec6be1e9f8197eb300e3c..cd1da649567100c4a3de2008d96c0d2d0ebd24ff 100644 --- a/diffusion/DiffPoolVec.cpp +++ b/diffusion/DiffPoolVec.cpp @@ -51,6 +51,12 @@ void DiffPoolVec::setN( unsigned int voxel, double v ) n_[ voxel ] = v; } +double DiffPoolVec::getPrev( unsigned int voxel ) const +{ + assert( voxel < n_.size() ); + return prev_[ voxel ]; +} + const vector< double >& DiffPoolVec::getNvec() const { return n_; @@ -71,6 +77,11 @@ void DiffPoolVec::setNvec( unsigned int start, unsigned int num, *p++ = *q++; } +void DiffPoolVec::setPrevVec() +{ + prev_ = n_; +} + double DiffPoolVec::getDiffConst() const { return diffConst_; @@ -142,5 +153,5 @@ void DiffPoolVec::advance( double dt ) void DiffPoolVec::reinit() // Not called by the clock, but by parent. { assert( n_.size() == nInit_.size() ); - n_ = nInit_; + prev_ = n_ = nInit_; } diff --git a/diffusion/DiffPoolVec.h b/diffusion/DiffPoolVec.h index 345403f691cb6a12bb581a576a43a62bfa664a72..587533eb35d0ba4715f4c7cf54947abb4359edff 100644 --- a/diffusion/DiffPoolVec.h +++ b/diffusion/DiffPoolVec.h @@ -26,6 +26,7 @@ class DiffPoolVec void setNinit( unsigned int vox, double value ); double getN( unsigned int vox ) const; void setN( unsigned int vox, double value ); + double getPrev( unsigned int vox ) const; double getDiffConst() const; void setDiffConst( double value ); @@ -46,6 +47,7 @@ class DiffPoolVec void setNvec( const vector< double >& n ); void setNvec( unsigned int start, unsigned int num, vector< double >::const_iterator q ); + void setPrevVec(); /// Assigns prev_ = n_ void setOps( const vector< Triplet< double > >& ops_, const vector< double >& diagVal_ ); /// Assign operations. @@ -53,6 +55,7 @@ class DiffPoolVec private: unsigned int id_; /// Integer conversion of Id of pool handled. vector< double > n_; /// Number of molecules of pool in each voxel + vector< double > prev_; /// # molecules of pool on previous timestep vector< double > nInit_; /// Boundary condition: Initial 'n'. double diffConst_; /// Diffusion const, assumed uniform double motorConst_; /// Motor const, ie, transport rate. diff --git a/diffusion/Dsolve.cpp b/diffusion/Dsolve.cpp index 9185ab6537ffab493c4df27be085420e8bc18671..61ffb1bc612c01dde5f917fd15faabd54169e0be 100644 --- a/diffusion/Dsolve.cpp +++ b/diffusion/Dsolve.cpp @@ -15,7 +15,9 @@ #include "../mesh/VoxelJunction.h" #include "XferInfo.h" #include "ZombiePoolInterface.h" +#include "../kinetics/ConcChan.h" #include "DiffPoolVec.h" +#include "ConcChanInfo.h" #include "FastMatrixElim.h" #include "../mesh/VoxelJunction.h" #include "DiffJunction.h" @@ -304,22 +306,9 @@ static double integ( double myN, double rf, double rb, double dt ) return myN; } -/** - * 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 Dsolve::calcJunction( const DiffJunction& jn, double dt ) +void Dsolve::calcJnDiff( const DiffJunction& jn, Dsolve* other, double dt) { - const double EPSILON = 1e-15; - Id oid( jn.otherDsolve ); - assert ( oid != Id() ); - assert ( oid.element()->cinfo()->isA( "Dsolve" ) ); - - Dsolve* other = reinterpret_cast< Dsolve* >( oid.eref().data() ); - + 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] ]; @@ -357,17 +346,143 @@ void Dsolve::calcJunction( const DiffJunction& jn, double dt ) } } +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 ); + } + } +} + +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. + // intPool is on self, extPool is on other, but we have a problem + // because the chanPool could be a third compartment, such as the memb + // If we stipulate it is is on self, that is easy but not general. + // 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 ]; + /* + DiffPoolVec& myDv = pools_[ jn.myPools[myChan.myPool] ]; + DiffPoolVec& otherDv = + other->pools_[ jn.otherPools[myChan.otherPool] ]; + DiffPoolVec& chanDv = pools_[ jn.myPools[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 perm = myChan.permeability * chanDv.getN( j->first ); + 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 perm = otherChan.permeability * chanDv.getN(j->second); + 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 ); + } + } +} + +/** + * 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. + * Note that we split the diffusion and channel calculations before and + * after then calcJnXfer calculations. This improves accuracy by 5x. + */ +void Dsolve::calcJunction( const DiffJunction& jn, double dt ) +{ + 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 ); + + 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 ); + + calcJnDiff( 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 ( vector< DiffJunction >::const_iterator - i = junctions_.begin(); i != junctions_.end(); ++i ) { - calcJunction( *i, p->dt ); - } } void Dsolve::reinit( const Eref& e, ProcPtr p ) @@ -378,6 +493,14 @@ void Dsolve::reinit( const Eref& e, ProcPtr p ) i->reinit(); } } + +void Dsolve::updateJunctions( double dt ) +{ + for ( vector< DiffJunction >::const_iterator + i = junctions_.begin(); i != junctions_.end(); ++i ) { + calcJunction( *i, dt ); + } +} ////////////////////////////////////////////////////////////// // Solver coordination and setup functions ////////////////////////////////////////////////////////////// @@ -417,6 +540,54 @@ void Dsolve::setStoich( Id id ) */ } } + 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(); + /* + ObjId inPool = i->element()->findCaller( fin ); + ObjId chanPool = i->element()->findCaller( fchan ); + ObjId outPool = i->element()->findCaller( fout ); + */ + if ( !( inPool.bad() or chanPool.bad() ) ) { + unsigned int inPoolIndex = convertIdToPoolIndex( inPool.id ); + unsigned int chanPoolIndex = convertIdToPoolIndex(chanPool.id); + if ( inPoolIndex != ~0U && chanPoolIndex != ~0U ) { + ConcChanInfo cci( inPoolIndex, outPool.id.value(), + chanPoolIndex, + Field< double >::get( *i, "permeability" ) + ); + channels_.push_back( cci ); + } + } + } } Id Dsolve::getStoich() const @@ -431,18 +602,18 @@ void Dsolve::setDsolve( Id dsolve ) void Dsolve::setCompartment( Id id ) { const Cinfo* c = id.element()->cinfo(); - if ( c->isA( "NeuroMesh" ) || c->isA( "SpineMesh" ) || - c->isA( "PsdMesh" ) || c->isA( "CylMesh" ) ) { - compartment_ = id; - numVoxels_ = Field< unsigned int >::get( id, "numMesh" ); - /* - const MeshCompt* m = reinterpret_cast< const MeshCompt* >( - id.eref().data() ); - numVoxels_ = m->getStencil().nRows(); - */ - } else { - cout << "Warning: Dsolve::setCompartment:: compartment must be " - "NeuroMesh or CylMesh, you tried :" << c->name() << endl; + 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; + } } } @@ -474,6 +645,8 @@ void Dsolve::makePoolMapFromElist( const vector< ObjId >& elist, 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 ); @@ -610,8 +783,8 @@ void Dsolve::buildNeuroMeshJunctions( const Eref& e, Id spineD, Id psdD ) return; } - innerBuildMeshJunctions( spineD, e.id() ); - innerBuildMeshJunctions( psdD, spineD ); + innerBuildMeshJunctions( spineD, e.id(), false ); + innerBuildMeshJunctions( psdD, spineD, false ); } void Dsolve::buildMeshJunctions( const Eref& e, Id other ) @@ -621,8 +794,10 @@ void Dsolve::buildMeshJunctions( const Eref& e, Id other ) otherMesh = Field< Id >::get( other, "compartment" ); if ( compartment_.element()->cinfo()->isA( "ChemCompt" ) && otherMesh.element()->cinfo()->isA( "ChemCompt" ) ) { - innerBuildMeshJunctions( e.id(), other ); - return; + bool isMembraneBound = + Field< bool >::get( compartment_, "isMembraneBound" ); + innerBuildMeshJunctions( e.id(), other, isMembraneBound ); + return; } } cout << "Warning: Dsolve::buildMeshJunctions: one of '" << @@ -644,14 +819,11 @@ void printJunction( Id self, Id other, const DiffJunction& jn ) } } -// Static utility func for building junctions -void Dsolve::innerBuildMeshJunctions( Id self, Id other ) +void Dsolve::mapDiffPoolsBetweenDsolves( DiffJunction& jn, + Id self, Id other) { - DiffJunction jn; // This is based on the Spine Dsolver. - jn.otherDsolve = other.value(); - // Map pools between Dsolves Dsolve* mySolve = reinterpret_cast< Dsolve* >( self.eref().data() ); - map< string, unsigned int > myPools; + 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() ); @@ -662,15 +834,83 @@ void Dsolve::innerBuildMeshJunctions( Id self, Id other ) other.eref().data() ); for ( unsigned int i = 0; i < otherSolve->pools_.size(); ++i ) { Id otherPool( otherSolve->pools_[i].getId() ); - map< string, unsigned int >::iterator p = + 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 + * bar_xfer_dest + */ +void Dsolve::mapXfersBetweenDsolves( + 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(); + 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() ); + vector< ConcChanInfo >& ch = channels_; + unsigned int outIndex; + for ( unsigned int i = 0; i < ch.size(); ++i ) { + outIndex = otherSolve->convertIdToPoolIndex( ch[i].otherPool ); + if ( outIndex != ~0U ) { + jn.myChannels.push_back(i); + ch[i].otherPool = outIndex; // replace the Id with the index. + } + } + // Now set up the other Dsolve. + vector< ConcChanInfo >& ch2 = otherSolve->channels_; + for ( unsigned int i = 0; i < ch2.size(); ++i ) { + outIndex = convertIdToPoolIndex( ch2[i].otherPool ); + if ( outIndex != ~0U ) { + jn.otherChannels.push_back(i); + ch2[i].otherPool = outIndex; // replace the Id with the index + } + } +} - // Map voxels between meshes. +static void mapVoxelsBetweenMeshes( DiffJunction& jn, Id self, Id other) +{ Id myMesh = Field< Id >::get( self, "compartment" ); Id otherMesh = Field< Id >::get( other, "compartment" ); @@ -686,9 +926,25 @@ void Dsolve::innerBuildMeshJunctions( Id self, Id other ) 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(); + 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 ); - mySolve->junctions_.push_back( jn ); + junctions_.push_back( jn ); } ///////////////////////////////////////////////////////////// @@ -712,18 +968,23 @@ void Dsolve::setNumAllVoxels( unsigned int num ) pools_[i].setNumVoxels( numVoxels_ ); } -unsigned int Dsolve::convertIdToPoolIndex( const Eref& e ) const +unsigned int Dsolve::convertIdToPoolIndex( const Id id ) const { - unsigned int i = e.id().value() - poolMapStart_; + unsigned int i = id.value() - poolMapStart_; if ( i < poolMap_.size() ) { return poolMap_[i]; } cout << "Warning: Dsolve::convertIdToPoollndex: Id out of range, (" << - poolMapStart_ << ", " << e.id() << ", " << + poolMapStart_ << ", " << id << ", " << id.path() << ", " << poolMap_.size() + poolMapStart_ << "\n"; return 0; } +unsigned int Dsolve::convertIdToPoolIndex( const Eref& e ) const +{ + return convertIdToPoolIndex( e.id() ); +} + void Dsolve::setN( const Eref& e, double v ) { unsigned int pid = convertIdToPoolIndex( e ); @@ -848,6 +1109,15 @@ void Dsolve::getBlock( vector< double >& values ) const } } +// 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(); + } +} + void Dsolve::setBlock( const vector< double >& values ) { unsigned int startVoxel = values[0]; diff --git a/diffusion/Dsolve.h b/diffusion/Dsolve.h index 0a16e7727432fe9aea86e117512e02a12d9c2caa..d6028e62a4c7fb8bdbbe267fa87f54f8174f2c16 100644 --- a/diffusion/Dsolve.h +++ b/diffusion/Dsolve.h @@ -67,6 +67,9 @@ class Dsolve: public ZombiePoolInterface 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 @@ -93,7 +96,18 @@ class Dsolve: public ZombiePoolInterface * 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 ); + 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 ); + void mapChansBetweenDsolves( DiffJunction& jn, Id self, Id other); /** * Computes flux through a junction between diffusion solvers. @@ -123,6 +137,7 @@ class Dsolve: public ZombiePoolInterface 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( @@ -137,6 +152,7 @@ class Dsolve: public ZombiePoolInterface ////////////////////////////////////////////////////////////////// // 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; @@ -156,6 +172,15 @@ class Dsolve: public ZombiePoolInterface */ 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 @@ -179,6 +204,8 @@ class Dsolve: public ZombiePoolInterface /// 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_; diff --git a/hsolve/HSolve.cpp b/hsolve/HSolve.cpp index 05d5ff8ecbe3bb3e0b35d71f806faf49826e6922..e4b9b0e74091ae27a2763d93dc604edecd682100 100644 --- a/hsolve/HSolve.cpp +++ b/hsolve/HSolve.cpp @@ -23,6 +23,7 @@ #include "../biophysics/ChanBase.h" #include "../biophysics/ChanCommon.h" #include "../biophysics/HHChannel.h" +#include "../biophysics/CaConc.h" #include "ZombieHHChannel.h" #include "../shell/Shell.h" @@ -192,6 +193,11 @@ HSolve::HSolve() ; } +HSolve::~HSolve() +{ + unzombify(); +} + /////////////////////////////////////////////////// // Dest function definitions @@ -215,23 +221,48 @@ void HSolve::zombify( Eref hsolve ) const for ( i = compartmentId_.begin(); i != compartmentId_.end(); ++i ) temp.push_back( ObjId( *i, 0 ) ); - for ( i = compartmentId_.begin(); i != compartmentId_.end(); ++i ) + for ( i = compartmentId_.begin(); i != compartmentId_.end(); ++i ) { CompartmentBase::zombify( i->eref().element(), ZombieCompartment::initCinfo(), hsolve.id() ); + } temp.clear(); for ( i = caConcId_.begin(); i != caConcId_.end(); ++i ) temp.push_back( ObjId( *i, 0 ) ); // Shell::dropClockMsgs( temp, "process" ); - for ( i = caConcId_.begin(); i != caConcId_.end(); ++i ) + for ( i = caConcId_.begin(); i != caConcId_.end(); ++i ) { CaConcBase::zombify( i->eref().element(), ZombieCaConc::initCinfo(), hsolve.id() ); + } temp.clear(); for ( i = channelId_.begin(); i != channelId_.end(); ++i ) temp.push_back( ObjId( *i, 0 ) ); - for ( i = channelId_.begin(); i != channelId_.end(); ++i ) + for ( i = channelId_.begin(); i != channelId_.end(); ++i ) { HHChannelBase::zombify( i->eref().element(), ZombieHHChannel::initCinfo(), hsolve.id() ); + } +} + +void HSolve::unzombify() const +{ + vector< Id >::const_iterator i; + + for ( i = compartmentId_.begin(); i != compartmentId_.end(); ++i ) + if ( i->element() ) { + CompartmentBase::zombify( i->eref().element(), + Compartment::initCinfo(), Id() ); + } + + for ( i = caConcId_.begin(); i != caConcId_.end(); ++i ) + if ( i->element() ) { + CaConcBase::zombify( i->eref().element(), CaConc::initCinfo(), Id() ); + } + + for ( i = channelId_.begin(); i != channelId_.end(); ++i ) + if ( i->element() ) { + HHChannelBase::zombify( i->eref().element(), + HHChannel::initCinfo(), Id() ); + } } void HSolve::setup( Eref hsolve ) diff --git a/hsolve/HSolve.h b/hsolve/HSolve.h index 9022a1f0dc579f796fcab9928d4c1b27e2477163..56c15d2dc712371a5f0ac3003a9b0cc0ff3a1bcb 100644 --- a/hsolve/HSolve.h +++ b/hsolve/HSolve.h @@ -17,6 +17,7 @@ class HSolve: public HSolveActive { public: HSolve(); + ~HSolve(); void process( const Eref& hsolve, ProcPtr p ); void reinit( const Eref& hsolve, ProcPtr p ); @@ -157,6 +158,7 @@ private: void setup( Eref hsolve ); void zombify( Eref hsolve ) const; + void unzombify() const; // Mapping global Id to local index. Defined in HSolveInterface.cpp. void mapIds(); diff --git a/hsolve/HSolveActive.cpp b/hsolve/HSolveActive.cpp index 9d34c6380024df603ba9e27e0bd34bb2263b046a..ebc62b340c7ca12538a0a108f805ff1e2352d09c 100644 --- a/hsolve/HSolveActive.cpp +++ b/hsolve/HSolveActive.cpp @@ -356,13 +356,13 @@ void HSolveActive::sendValues( ProcPtr info ) { vector< unsigned int >::iterator i; - for ( i = outVm_.begin(); i != outVm_.end(); ++i ) + for ( i = outVm_.begin(); i != outVm_.end(); ++i ) { Compartment::VmOut()->send( //~ ZombieCompartment::VmOut()->send( compartmentId_[ *i ].eref(), V_[ *i ] ); - + } for ( i = outIk_.begin(); i != outIk_.end(); ++i ){ diff --git a/hsolve/HSolveUtils.cpp b/hsolve/HSolveUtils.cpp index 77e0cc108dc882c059c9577c9ec863fd95958a9b..5038c6e4357a7b9a0b23fb2630a463a2d687db30 100644 --- a/hsolve/HSolveUtils.cpp +++ b/hsolve/HSolveUtils.cpp @@ -320,9 +320,9 @@ int HSolveUtils::targets( e->getNeighbors( all, f ); vector< Id >::iterator ia; - if ( filter.empty() ) + if ( filter.empty() ) { target.insert( target.end(), all.begin(), all.end() ); - else + } else { for ( ia = all.begin(); ia != all.end(); ++ia ) { string className = (*ia).element()->cinfo()->name(); bool hit = @@ -335,6 +335,7 @@ int HSolveUtils::targets( if ( ( hit && include ) || ( !hit && !include ) ) target.push_back( *ia ); } + } return target.size() - oldSize; } diff --git a/kinetics/CMakeLists.txt b/kinetics/CMakeLists.txt index 403f360349e71b083dd0d10c3383c4eae0a96945..b836312c7e44d67aacfddbcdca578517b42a6f75 100644 --- a/kinetics/CMakeLists.txt +++ b/kinetics/CMakeLists.txt @@ -11,6 +11,7 @@ add_library(kinetics Enz.cpp MMenz.cpp Species.cpp + ConcChan.cpp ReadKkit.cpp WriteKkit.cpp ReadCspace.cpp diff --git a/kinetics/ConcChan.cpp b/kinetics/ConcChan.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6217084580e80802edc8b61af191ddb5bee74b6a --- /dev/null +++ b/kinetics/ConcChan.cpp @@ -0,0 +1,206 @@ +/********************************************************************** +** This program is part of 'MOOSE', the +** Messaging Object Oriented Simulation Environment. +** 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. +**********************************************************************/ + +#include "header.h" +#include "ConcChan.h" + +static SrcFinfo2< double, double > *inPoolOut() { + static SrcFinfo2< double, double > inPoolOut( + "inPoolOut", + "Sends out increment to molecules on inside of membrane" + ); + return &inPoolOut; +} + +static SrcFinfo2< double, double > *outPoolOut() { + static SrcFinfo2< double, double > outPoolOut( + "outPoolOut", + "Sends out increment to molecules on outside of membrane" + ); + return &outPoolOut; +} + +const Cinfo* ConcChan::initCinfo() +{ + ////////////////////////////////////////////////////////////// + // Field Definitions + ////////////////////////////////////////////////////////////// + static ValueFinfo< ConcChan, double > permeability( + "permeability", + "Permability, in units of vol/(#s) i.e., 1/(numconc.s) " + "Flux (#/s) = permeability * N * (#out/vol_out - #in/vol_in)", + &ConcChan::setPermeability, + &ConcChan::getPermeability + ); + static ValueFinfo< ConcChan, double > numChan( + "numChan", + "numChan is the number of molecules of the channel.", + &ConcChan::setNumChan, + &ConcChan::getNumChan + ); + static ReadOnlyValueFinfo< ConcChan, double > flux( + "flux", + "Flux of molecules through channel, in units of #/s ", + &ConcChan::getFlux + ); + + ////////////////////////////////////////////////////////////// + // MsgDest Definitions + ////////////////////////////////////////////////////////////// + static DestFinfo process( "process", + "Handles process call", + new ProcOpFunc< ConcChan >( &ConcChan::process ) ); + static DestFinfo reinit( "reinit", + "Handles reinit call", + new ProcOpFunc< ConcChan >( &ConcChan::reinit ) ); + + ////////////////////////////////////////////////////////////// + // Shared Msg Definitions + ////////////////////////////////////////////////////////////// + + static DestFinfo inPool( "inPool", + "Handles # of molecules of pool inside membrane", + new OpFunc1< ConcChan, double >( &ConcChan::inPool ) ); + static DestFinfo outPool( "outPool", + "Handles # of molecules of pool outside membrane", + new OpFunc1< ConcChan, double >( &ConcChan::outPool ) ); + static Finfo* inShared[] = { + inPoolOut(), &inPool + }; + static Finfo* outShared[] = { + outPoolOut(), &outPool + }; + static SharedFinfo in( "in", + "Connects to pool on inside", + inShared, sizeof( inShared ) / sizeof( const Finfo* ) + ); + static SharedFinfo out( "out", + "Connects to pool on outside", + outShared, sizeof( outShared ) / sizeof( const Finfo* ) + ); + static Finfo* procShared[] = { + &process, &reinit + }; + static SharedFinfo proc( "proc", + "Shared message for process and reinit", + procShared, sizeof( procShared ) / sizeof( const Finfo* ) + ); + + + static Finfo* concChanFinfos[] = { + &permeability, // Value + &numChan, // Value + &flux, // ReadOnlyValue + &in, // SharedFinfo + &out, // SharedFinfo + &proc, // SharedFinfo + }; + + static string doc[] = + { + "Name", "ConcChan", + "Author", "Upinder S. Bhalla, 2018, NCBS", + "Description", "Channel for molecular flow between cellular " + "compartments. Need not be ions, and the flux is not a current, " + "but number of molecules/sec. ", + }; + + static Dinfo< ConcChan > dinfo; + static Cinfo concChanCinfo ( + "ConcChan", + Neutral::initCinfo(), + concChanFinfos, + sizeof( concChanFinfos ) / sizeof ( Finfo* ), + &dinfo, + doc, + sizeof( doc ) / sizeof( string ) + ); + + return &concChanCinfo; +} + + static const Cinfo* concChanCinfo = ConcChan::initCinfo(); + +////////////////////////////////////////////////////////////// +// ConcChan internal functions +////////////////////////////////////////////////////////////// + + +ConcChan::ConcChan( ) + : permeability_( 0.1 ), + n_( 0.0 ), + flux_( 0.0 ), + charge_( 0.0 ), + Vm_( 0.0 ) +{ + ; +} + +ConcChan::~ConcChan( ) +{;} + +////////////////////////////////////////////////////////////// +// MsgDest Definitions +////////////////////////////////////////////////////////////// + + +void ConcChan::process( const Eref& e, ProcPtr p ) +{ +} + +void ConcChan::reinit( const Eref& e, ProcPtr p ) +{ +} + +void ConcChan::inPool( double conc ) +{ +} + +void ConcChan::outPool( double conc ) +{ +} + +////////////////////////////////////////////////////////////// +// Field Definitions +////////////////////////////////////////////////////////////// + +void ConcChan::setPermeability( double v ) +{ + permeability_ = v; +} + +double ConcChan::getPermeability() const +{ + return permeability_; +} + +void ConcChan::setNumChan( double v ) +{ + n_ = v; +} + +double ConcChan::getNumChan() const +{ + return n_; +} + +double ConcChan::getFlux() const +{ + return flux_; +} + +////////////////////////////////////////////////////////////// +// Utility function +////////////////////////////////////////////////////////////// +double ConcChan::flux( double inConc, double outConc, double dt ) +{ + return permeability_ * n_ * ( outConc - inConc ) * dt; + // Later add stuff for voltage dependence. + // Also use exp Euler +} diff --git a/kinetics/ConcChan.h b/kinetics/ConcChan.h new file mode 100644 index 0000000000000000000000000000000000000000..6ebf48b417f9b8711d0805edf2a03d9a2f888d97 --- /dev/null +++ b/kinetics/ConcChan.h @@ -0,0 +1,52 @@ +/********************************************************************** +** This program is part of 'MOOSE', the +** Messaging Object Oriented Simulation Environment. +** Copyright (C) 2018 Upinder S. Bhalla. and NCBS +** It is made available under the terms of the +** GNU Public License version 3 or later. +** See the file COPYING.LIB for the full notice. +**********************************************************************/ + +#ifndef _CONC_CHAN_H +#define _CONC_CHAN_H + +class ConcChan +{ + public: + ConcChan(); + ~ConcChan(); + + + ////////////////////////////////////////////////////////////////// + // Field assignment stuff + ////////////////////////////////////////////////////////////////// + + void setPermeability( double v ); + double getPermeability() const; + void setNumChan( double v ); + double getNumChan() const; + double getFlux() const; + + ////////////////////////////////////////////////////////////////// + // Dest funcs + ////////////////////////////////////////////////////////////////// + + void process( const Eref& e, ProcPtr p ); + void reinit( const Eref& e, ProcPtr p ); + void inPool( double conc ); + void outPool( double conc ); + ////////////////////////////////////////////////////////////////// + // Utility function + ////////////////////////////////////////////////////////////////// + double flux( double inConc, double outConc, double dt ); + + static const Cinfo* initCinfo(); + protected: + double permeability_; /// permeability in vol/(#.s) units. + double n_; /// Number of molecules of channel. + double flux_; /// Flux of molecule through channel + double charge_; /// Later: for including Nernst calculations + double Vm_; /// Later: for including Nernst calculations +}; + +#endif // CONC_CHAN_H diff --git a/kinetics/EnzBase.cpp b/kinetics/EnzBase.cpp index 0c4c136ceae179987e213bdcf60aa89d1bc21198..0812374c50886a4fe10587186fda234f54eca9d3 100644 --- a/kinetics/EnzBase.cpp +++ b/kinetics/EnzBase.cpp @@ -63,6 +63,12 @@ const Cinfo* EnzBase::initCinfo() &EnzBase::getNumSub ); + static ReadOnlyElementValueFinfo< EnzBase, unsigned int > numPrd( + "numProducts", + "Number of products in this MM reaction. Usually 1.", + &EnzBase::getNumPrd + ); + ////////////////////////////////////////////////////////////// // Shared Msg Definitions @@ -125,6 +131,7 @@ const Cinfo* EnzBase::initCinfo() &numKm, // ElementValue &kcat, // Value &numSub, // ReadOnlyElementValue + &numPrd, // ReadOnlyElementValue &enzDest, // DestFinfo &sub, // SharedFinfo &prd, // SharedFinfo @@ -259,6 +266,14 @@ unsigned int EnzBase::getNumSub( const Eref& e ) const return ( mfb->size() ); } +unsigned int EnzBase::getNumPrd( const Eref& e ) const +{ + const vector< MsgFuncBinding >* mfb = + e.element()->getMsgAndFunc( prdOut()->getBindIndex() ); + assert( mfb ); + return ( mfb->size() ); +} + //////////////////////////////////////////////////////////////////////// // Zombie conversion routine to convert between Enz subclasses. //////////////////////////////////////////////////////////////////////// diff --git a/kinetics/EnzBase.h b/kinetics/EnzBase.h index 791e05322e327fb7a1827d755d2dfec720e1323f..bd5f37af13e0979d5ab909accf9255bdee4e9322 100644 --- a/kinetics/EnzBase.h +++ b/kinetics/EnzBase.h @@ -33,6 +33,7 @@ class EnzBase // This doesn't need a virtual form, depends on standard msgs. unsigned int getNumSub( const Eref& e ) const; + unsigned int getNumPrd( const Eref& e ) const; ////////////////////////////////////////////////////////////////// // Virtual funcs for field assignment stuff diff --git a/kinetics/ReadKkit.cpp b/kinetics/ReadKkit.cpp index 2529919a8714fb64013324656d13a545bfda446d..d3b5bd4154067e43753f4a4e60d1453da2bb7b4d 100644 --- a/kinetics/ReadKkit.cpp +++ b/kinetics/ReadKkit.cpp @@ -195,6 +195,7 @@ void makeSolverOnCompt( Shell* s, const vector< ObjId >& compts, Field< Id >::set( stoich, "ksolve", ksolve ); Field< string >::set( stoich, "path", simpath ); } + /* Not needed now that we use xfer pools to cross compartments. if ( stoichVec.size() == 2 ) { SetGet1< Id >::set( stoichVec[1], "buildXreacs", stoichVec[0] ); } @@ -206,6 +207,7 @@ void makeSolverOnCompt( Shell* s, const vector< ObjId >& compts, i = stoichVec.begin(); i != stoichVec.end(); ++i ) { SetGet0::set( *i, "filterXreacs" ); } + */ } void setMethod( Shell* s, Id mgr, double simdt, double plotdt, @@ -286,31 +288,9 @@ Id ReadKkit::read( assignEnzCompartments(); assignMMenzCompartments(); - /* - if ( moveOntoCompartment_ ) { - assignPoolCompartments(); - assignReacCompartments(); - assignEnzCompartments(); - assignMMenzCompartments(); - } - */ - convertParametersToConcUnits(); - // s->doSetClock( 8, plotdt_ ); - - // string plotpath = basePath_ + "/graphs/##[TYPE=Table]," + basePath_ + "/moregraphs/##[TYPE=Table]"; - // s->doUseClock( plotpath, "process", 8 ); - setMethod( s, mgr, simdt_, plotdt_, method ); - /* - if ( !moveOntoCompartment_ ) { - assignPoolCompartments(); - assignReacCompartments(); - assignEnzCompartments(); - assignMMenzCompartments(); - } - */ //Harsha: Storing solver and runtime at model level rather than model level Id kinetics( basePath_+"/kinetics"); @@ -332,15 +312,6 @@ void ReadKkit::run() shell_->doSetClock( 16, plotdt_ ); shell_->doSetClock( 17, plotdt_ ); shell_->doSetClock( 18, plotdt_ ); - /* - string poolpath = basePath_ + "/kinetics/##[ISA=Pool]"; - string reacpath = basePath_ + "/kinetics/##[ISA!=Pool]"; - string plotpath = basePath_ + "/graphs/##[TYPE=Table]," + - basePath_ + "/moregraphs/##[TYPE=Table]"; - shell_->doUseClock( reacpath, "process", 4 ); - shell_->doUseClock( poolpath, "process", 5 ); - shell_->doUseClock( plotpath, "process", 8 ); - */ shell_->doReinit(); if ( useVariableDt_ ) { shell_->doSetClock( 11, fastdt_ ); @@ -540,16 +511,33 @@ string ReadKkit::cleanPath( const string& path ) const // which later created a problem as the same name exist in moose when minus // was replaced with underscore. //So replacing minus with _minus_ like I do in SBML - string ret = path; + size_t Iindex = 0; + string cleanDigit="/"; + while(true) + { + size_t sindex = path.find('/',Iindex+1); + if (sindex == string::npos) + { if (isdigit((path.substr(Iindex+1,sindex-Iindex-1))[0]) ) + cleanDigit += '_' ; + cleanDigit += path.substr(Iindex+1,sindex-Iindex-1); + break; + } + if (isdigit((path.substr(Iindex+1,sindex-Iindex-1))[0])) + cleanDigit+='_'; + cleanDigit += path.substr(Iindex+1,sindex-Iindex); + Iindex = sindex; + } + string ret = cleanDigit; + //string ret = path; string cleanString; - for ( unsigned int i = 0; i < path.length(); ++i ) { + for ( unsigned int i = 0; i < cleanDigit.length(); ++i ) { char c = ret[i]; if ( c == '*' ) cleanString += "_p"; else if ( c == '[' || c == ']' || c == '@' || c == ' ') cleanString += '_'; else if (c == '-') - cleanString += "_dash_"; + cleanString += "_"; else cleanString += c; } @@ -563,8 +551,7 @@ void assignArgs( map< string, int >& argConv, const vector< string >& args ) } void ReadKkit::objdump( const vector< string >& args) -{ - if ( args[1] == "kpool" ) +{ if ( args[1] == "kpool" ) assignArgs( poolMap_, args ); else if ( args[1] == "kreac" ) assignArgs( reacMap_, args ); @@ -619,8 +606,7 @@ void ReadKkit::textload( const vector< string >& args) } void ReadKkit::undump( const vector< string >& args) -{ - if ( args[1] == "kpool" ) +{ if ( args[1] == "kpool" ) buildPool( args ); else if ( args[1] == "kreac" ) buildReac( args ); @@ -973,7 +959,6 @@ Id ReadKkit::buildInfo( Id parent, map< string, int >& m, const vector< string >& args ) { Id info = shell_->doCreate( "Annotator", parent, "info", 1 ); - //cout << "parent " << parent << " " << parent.path() << " info " << info.path(); assert( info != Id() ); double x = atof( args[ m[ "x" ] ].c_str() ); @@ -991,7 +976,6 @@ Id ReadKkit::buildGroup( const vector< string >& args ) { string head; string tail = pathTail( cleanPath( args[2] ), head ); - Id pa = shell_->doFind( head ).id; assert( pa != Id() ); Id group = shell_->doCreate( "Neutral", pa, tail, 1 ); @@ -1012,7 +996,7 @@ Id ReadKkit::buildGroup( const vector< string >& args ) * traffic are originally framed in terms of number of receptors, not conc. */ Id ReadKkit::buildPool( const vector< string >& args ) -{ +{ string head; string clean = cleanPath( args[2] ); string tail = pathTail( clean, head ); @@ -1060,7 +1044,6 @@ Id ReadKkit::buildPool( const vector< string >& args ) poolVols_[pool] = vol; Id info = buildInfo( pool, poolMap_, args ); - /* cout << setw( 20 ) << head << setw( 15 ) << tail << " " << setw( 12 ) << nInit << " " << diff --git a/ksolve/Gsolve.cpp b/ksolve/Gsolve.cpp index 42bee6f72bf4d4de08e0d8724388eb0ce7a56764..25a5c060e7fcc6cb14c6be0550a1121b5842d548 100644 --- a/ksolve/Gsolve.cpp +++ b/ksolve/Gsolve.cpp @@ -33,23 +33,6 @@ const unsigned int OFFNODE = ~0; -// static function -SrcFinfo2< Id, vector< double > >* Gsolve::xComptOut() -{ - static SrcFinfo2< Id, vector< double > > xComptOut( "xComptOut", - "Sends 'n' of all molecules participating in cross-compartment " - "reactions between any juxtaposed voxels between current compt " - "and another compartment. This includes molecules local to this " - "compartment, as well as proxy molecules belonging elsewhere. " - "A(t+1) = (Alocal(t+1) + AremoteProxy(t+1)) - Alocal(t) " - "A(t+1) = (Aremote(t+1) + Aproxy(t+1)) - Aproxy(t) " - "Then we update A on the respective solvers with: " - "Alocal(t+1) = Aproxy(t+1) = A(t+1) " - "This is equivalent to sending dA over on each timestep. " - ); - return &xComptOut; -} - const Cinfo* Gsolve::initCinfo() { /////////////////////////////////////////////////////// @@ -161,11 +144,6 @@ const Cinfo* Gsolve::initCinfo() "Handles initReinit call from Clock", new ProcOpFunc< Gsolve >( &Gsolve::initReinit ) ); - static DestFinfo xComptIn( "xComptIn", - "Handles arriving pool 'n' values used in cross-compartment " - "reactions.", - new EpFunc2< Gsolve, Id, vector< double > >( &Gsolve::xComptIn ) - ); /////////////////////////////////////////////////////// // Shared definitions /////////////////////////////////////////////////////// @@ -188,16 +166,6 @@ const Cinfo* Gsolve::initCinfo() initShared, sizeof( initShared ) / sizeof( const Finfo* ) ); - static Finfo* xComptShared[] = - { - xComptOut(), &xComptIn - }; - static SharedFinfo xCompt( "xCompt", - "Shared message for pool exchange for cross-compartment " - "reactions. Exchanges latest values of all pools that " - "participate in such reactions.", - xComptShared, sizeof( xComptShared ) / sizeof( const Finfo* ) - ); /////////////////////////////////////////////////////// static Finfo* gsolveFinfos[] = @@ -210,7 +178,6 @@ const Cinfo* Gsolve::initCinfo() &voxelVol, // DestFinfo &proc, // SharedFinfo &init, // SharedFinfo - &xCompt, // SharedFinfo // Here we put new fields that were not there in the Ksolve. &useRandInit, // Value &useClockedUpdate, // Value @@ -438,6 +405,7 @@ void Gsolve::process( const Eref& e, ProcPtr p ) dvalues[2] = 0; dvalues[3] = stoichPtr_->getNumVarPools(); dsolvePtr_->getBlock( dvalues ); + dsolvePtr_->setPrev(); // Here we need to convert to integers, just in case. Normally // one would use a stochastic (integral) diffusion method with @@ -459,31 +427,10 @@ void Gsolve::process( const Eref& e, ProcPtr p ) } setBlock( dvalues ); } - // Second, take the arrived xCompt reac values and update S with them. - // Here the roundoff issues are handled by the GssaVoxelPools functions - for ( unsigned int i = 0; i < xfer_.size(); ++i ) - { - XferInfo& xf = xfer_[i]; - // cout << xfer_.size() << " " << xf.xferVoxel.size() << endl; - for ( unsigned int j = 0; j < xf.xferVoxel.size(); ++j ) - { - pools_[xf.xferVoxel[j]].xferIn( xf, j, &sys_ ); - } - } - // Third, record the current value of pools as the reference for the - // next cycle. - for ( unsigned int i = 0; i < xfer_.size(); ++i ) - { - XferInfo& xf = xfer_[i]; - for ( unsigned int j = 0; j < xf.xferVoxel.size(); ++j ) - { - pools_[xf.xferVoxel[j]].xferOut( j, xf.lastValues, xf.xferPoolIdx ); - } - } - // Fourth: Fix the rates if we have had any diffusion or xreacs + // Third: Fix the rates if we have had any diffusion or xreacs // happening. This is very inefficient at this point, need to fix. - if ( dsolvePtr_ || xfer_.size() > 0 ) + if ( dsolvePtr_ ) { for ( vector< GssaVoxelPools >::iterator i = pools_.begin(); i != pools_.end(); ++i ) @@ -491,7 +438,7 @@ void Gsolve::process( const Eref& e, ProcPtr p ) i->refreshAtot( &sys_ ); } } - // Fifth, update the mol #s. + // Fourth, update the mol #s. // First we advance the simulation. size_t nvPools = pools_.size( ); @@ -537,6 +484,11 @@ void Gsolve::process( const Eref& e, ProcPtr p ) kvalues[3] = stoichPtr_->getNumVarPools(); 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 } } @@ -552,30 +504,7 @@ void Gsolve::reinit( const Eref& e, ProcPtr p ) { i->reinit( &sys_ ); } - - // Second, take the arrived xCompt reac values and update S with them. - // Here the roundoff issues are handled by the GssaVoxelPools functions - for ( unsigned int i = 0; i < xfer_.size(); ++i ) - { - const XferInfo& xf = xfer_[i]; - for ( unsigned int j = 0; j < xf.xferVoxel.size(); ++j ) - { - pools_[xf.xferVoxel[j]].xferInOnlyProxies( - xf.xferPoolIdx, xf.values, - stoichPtr_->getNumProxyPools(), j ); - } - } - // Third, record the current value of pools as the reference for the - // next cycle. - for ( unsigned int i = 0; i < xfer_.size(); ++i ) - { - XferInfo& xf = xfer_[i]; - for ( unsigned int j = 0; j < xf.xferVoxel.size(); ++j ) - { - pools_[xf.xferVoxel[j]].xferOut( j, xf.lastValues, xf.xferPoolIdx ); - } - } - // Fourth, update the atots. + // Second, update the atots. for ( vector< GssaVoxelPools >::iterator i = pools_.begin(); i != pools_.end(); ++i ) { @@ -593,24 +522,7 @@ void Gsolve::reinit( const Eref& e, ProcPtr p ) // init operations. ////////////////////////////////////////////////////////////// void Gsolve::initProc( const Eref& e, ProcPtr p ) -{ - if ( !stoichPtr_ ) - return; - // vector< vector< double > > values( xfer_.size() ); - for ( unsigned int i = 0; i < xfer_.size(); ++i ) - { - XferInfo& xf = xfer_[i]; - unsigned int size = xf.xferPoolIdx.size() * xf.xferVoxel.size(); - // values[i].resize( size, 0.0 ); - vector< double > values( size, 0.0 ); - for ( unsigned int j = 0; j < xf.xferVoxel.size(); ++j ) - { - unsigned int vox = xf.xferVoxel[j]; - pools_[vox].xferOut( j, values, xf.xferPoolIdx ); - } - xComptOut()->sendTo( e, xf.ksolve, e.id(), values ); - } -} +{;} void Gsolve::initReinit( const Eref& e, ProcPtr p ) { @@ -620,20 +532,6 @@ void Gsolve::initReinit( const Eref& e, ProcPtr p ) { pools_[i].reinit( &sys_ ); } - // vector< vector< double > > values( xfer_.size() ); - for ( unsigned int i = 0; i < xfer_.size(); ++i ) - { - XferInfo& xf = xfer_[i]; - unsigned int size = xf.xferPoolIdx.size() * xf.xferVoxel.size(); - xf.lastValues.assign( size, 0.0 ); - for ( unsigned int j = 0; j < xf.xferVoxel.size(); ++j ) - { - unsigned int vox = xf.xferVoxel[j]; - pools_[ vox ].xferOut( j, xf.lastValues, xf.xferPoolIdx ); - // values[i] = xf.lastValues; - } - xComptOut()->sendTo( e, xf.ksolve, e.id(), xf.lastValues ); - } } ////////////////////////////////////////////////////////////// // Solver setup @@ -1118,7 +1016,6 @@ void Gsolve::updateVoxelVol( vector< double > vols ) { pools_[i].setVolumeAndDependencies( vols[i] ); } - stoichPtr_->setupCrossSolverReacVols(); updateRateTerms( ~0U ); } } @@ -1130,7 +1027,6 @@ void Gsolve::updateRateTerms( unsigned int index ) // unsigned int numCrossRates = stoichPtr_->getNumRates() - stoichPtr_->getNumCoreRates(); for ( unsigned int i = 0 ; i < pools_.size(); ++i ) { - // pools_[i].resetXreacScale( numCrossRates ); pools_[i].updateAllRateTerms( stoichPtr_->getRateTerms(), stoichPtr_->getNumCoreRates() ); } diff --git a/ksolve/Gsolve.h b/ksolve/Gsolve.h index 9cf59a008d9c9159b8470ce5f6ac859900d628c4..78f572abc3a32d03af85a67812f67bb34ee04398 100644 --- a/ksolve/Gsolve.h +++ b/ksolve/Gsolve.h @@ -129,7 +129,6 @@ public: #endif ////////////////////////////////////////////////////////////////// - static SrcFinfo2< Id, vector< double > >* xComptOut(); static const Cinfo* initCinfo(); private: diff --git a/ksolve/GssaVoxelPools.cpp b/ksolve/GssaVoxelPools.cpp index 438652d8af1f748a9226d4befabbd04a0de234ce..273964301a5e40d0a8b853fdee1b198ccd3c616f 100644 --- a/ksolve/GssaVoxelPools.cpp +++ b/ksolve/GssaVoxelPools.cpp @@ -343,7 +343,6 @@ void GssaVoxelPools::setStoich( const Stoich* stoichPtr ) void GssaVoxelPools::setVolumeAndDependencies( double vol ) { VoxelPoolsBase::setVolumeAndDependencies( vol ); - stoichPtr_->setupCrossSolverReacVols(); updateAllRateTerms( stoichPtr_->getRateTerms(), stoichPtr_->getNumCoreRates() ); } @@ -415,30 +414,3 @@ void GssaVoxelPools::xferIn( XferInfo& xf, // Does this fix the problem of negative concs? refreshAtot( g ); } - -void GssaVoxelPools::xferInOnlyProxies( - const vector< unsigned int >& poolIndex, - const vector< double >& values, - unsigned int numProxyPools, - unsigned int voxelIndex ) -{ - unsigned int offset = voxelIndex * poolIndex.size(); - vector< double >::const_iterator i = values.begin() + offset; - unsigned int proxyEndIndex = stoichPtr_->getNumVarPools() + - stoichPtr_->getNumProxyPools(); - for ( vector< unsigned int >::const_iterator - k = poolIndex.begin(); k != poolIndex.end(); ++k ) - { - // if ( *k >= size() - numProxyPools ) - if ( *k >= stoichPtr_->getNumVarPools() && *k < proxyEndIndex ) - { - double base = floor( *i ); - if ( rng_.uniform() >= (*i - base) ) - varSinit()[*k] = (varS()[*k] += base ); - else - varSinit()[*k] = (varS()[*k] += base + 1.0 ); - // varSinit()[*k] = (varS()[*k] += round( *i )); - } - i++; - } -} diff --git a/ksolve/GssaVoxelPools.h b/ksolve/GssaVoxelPools.h index 1d3a3dbac7d18c341ddc3e64002c307753758121..716698a48037aab51363745fc19348b9536f415b 100644 --- a/ksolve/GssaVoxelPools.h +++ b/ksolve/GssaVoxelPools.h @@ -70,17 +70,6 @@ public: void xferIn( XferInfo& xf, unsigned int voxelIndex, const GssaSystem* g ); - /** - * Used during initialization: Takes only the proxy pool values - * from the incoming transfer data, and assigns it to the proxy - * pools on current solver - */ - void xferInOnlyProxies( - const vector< unsigned int >& poolIndex, - const vector< double >& values, - unsigned int numProxyPools, - unsigned int voxelIndex ); - void setStoich( const Stoich* stoichPtr ); private: diff --git a/ksolve/Ksolve.cpp b/ksolve/Ksolve.cpp index a7ee4c7adb45109227d2262613ec202426dcba6d..8b7b5ac9ddeaa261724ad8eda3954cfc496d2010 100644 --- a/ksolve/Ksolve.cpp +++ b/ksolve/Ksolve.cpp @@ -17,7 +17,6 @@ #include "VoxelPoolsBase.h" #include "VoxelPools.h" #include "../mesh/VoxelJunction.h" -#include "XferInfo.h" #include "ZombiePoolInterface.h" #include "RateTerm.h" @@ -38,23 +37,6 @@ const unsigned int OFFNODE = ~0; -// static function -SrcFinfo2< Id, vector< double > >* Ksolve::xComptOut() -{ - static SrcFinfo2< Id, vector< double > > xComptOut( "xComptOut", - "Sends 'n' of all molecules participating in cross-compartment " - "reactions between any juxtaposed voxels between current compt " - "and another compartment. This includes molecules local to this " - "compartment, as well as proxy molecules belonging elsewhere. " - "A(t+1) = (Alocal(t+1) + AremoteProxy(t+1)) - Alocal(t) " - "A(t+1) = (Aremote(t+1) + Aproxy(t+1)) - Aproxy(t) " - "Then we update A on the respective solvers with: " - "Alocal(t+1) = Aproxy(t+1) = A(t+1) " - "This is equivalent to sending dA over on each timestep. " - ); - return &xComptOut; -} - const Cinfo* Ksolve::initCinfo() { /////////////////////////////////////////////////////// @@ -193,22 +175,6 @@ const Cinfo* Ksolve::initCinfo() initShared, sizeof( initShared ) / sizeof( const Finfo* ) ); - static DestFinfo xComptIn( "xComptIn", - "Handles arriving pool 'n' values used in cross-compartment " - "reactions.", - new EpFunc2< Ksolve, Id, vector< double > >( &Ksolve::xComptIn ) - ); - static Finfo* xComptShared[] = - { - xComptOut(), &xComptIn - }; - static SharedFinfo xCompt( "xCompt", - "Shared message for pool exchange for cross-compartment " - "reactions. Exchanges latest values of all pools that " - "participate in such reactions.", - xComptShared, sizeof( xComptShared ) / sizeof( const Finfo* ) - ); - static Finfo* ksolveFinfos[] = { &method, // Value @@ -225,7 +191,6 @@ const Cinfo* Ksolve::initCinfo() &estimatedDt, // ReadOnlyValue &stoich, // ReadOnlyValue &voxelVol, // DestFinfo - &xCompt, // SharedFinfo &proc, // SharedFinfo &init, // SharedFinfo }; @@ -542,47 +507,17 @@ void Ksolve::process( const Eref& e, ProcPtr p ) dvalues[1] = getNumLocalVoxels(); dvalues[2] = 0; dvalues[3] = stoichPtr_->getNumVarPools(); - dsolvePtr_->getBlock( dvalues ); - - /* - vector< double >::iterator i = dvalues.begin() + 4; - for ( ; i != dvalues.end(); ++i ) - cout << *i << " " << round( *i ) << endl; - getBlock( kvalues ); - vector< double >::iterator d = dvalues.begin() + 4; - for ( vector< double >::iterator - k = kvalues.begin() + 4; k != kvalues.end(); ++k ) - *k++ = ( *k + *d )/2.0 - setBlock( kvalues ); - */ - setBlock( dvalues ); - } - - // Second, take the arrived xCompt reac values and update S with them. - for ( unsigned int i = 0; i < xfer_.size(); ++i ) - { - const XferInfo& xf = xfer_[i]; - // cout << xfer_.size() << " " << xf.xferVoxel.size() << endl; - for ( unsigned int j = 0; j < xf.xferVoxel.size(); ++j ) - { - pools_[xf.xferVoxel[j]].xferIn( - xf.xferPoolIdx, xf.values, xf.lastValues, j ); - } - } - // Third, record the current value of pools as the reference for the - // next cycle. - for ( unsigned int i = 0; i < xfer_.size(); ++i ) - { - XferInfo& xf = xfer_[i]; - for ( unsigned int j = 0; j < xf.xferVoxel.size(); ++j ) - pools_[xf.xferVoxel[j]].xferOut( j, xf.lastValues, xf.xferPoolIdx ); + dsolvePtr_->getBlock( dvalues ); + // Second, set the prev_ value in DiffPoolVec + dsolvePtr_->setPrev(); + setBlock( dvalues ); } size_t nvPools = pools_.size( ); #ifdef PARALLELIZE_KSOLVE_WITH_CPP11_ASYNC - // Fourth, do the numerical integration for all reactions. + // Third, do the numerical integration for all reactions. size_t grainSize = min( nvPools, 1 + (nvPools / numThreads_ ) ); size_t nWorkers = nvPools / grainSize; @@ -615,7 +550,7 @@ void Ksolve::process( const Eref& e, ProcPtr p ) #endif - // Finally, assemble and send the integrated values off for the Dsolve. + // Assemble and send the integrated values off for the Dsolve. if ( dsolvePtr_ ) { vector< double > kvalues( 4 ); @@ -625,6 +560,10 @@ void Ksolve::process( const Eref& e, ProcPtr p ) kvalues[3] = stoichPtr_->getNumVarPools(); 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 ); } } @@ -674,28 +613,6 @@ void Ksolve::reinit( const Eref& e, ProcPtr p ) return; } - // cout << "************************* path = " << e.id().path() << endl; - for ( unsigned int i = 0; i < xfer_.size(); ++i ) - { - const XferInfo& xf = xfer_[i]; - for ( unsigned int j = 0; j < xf.xferVoxel.size(); ++j ) - { - pools_[xf.xferVoxel[j]].xferInOnlyProxies( - xf.xferPoolIdx, xf.values, - stoichPtr_->getNumProxyPools(), - j ); - } - } - for ( unsigned int i = 0; i < xfer_.size(); ++i ) - { - XferInfo& xf = xfer_[i]; - for ( unsigned int j = 0; j < xf.xferVoxel.size(); ++j ) - { - pools_[xf.xferVoxel[j]].xferOut( - j, xf.lastValues, xf.xferPoolIdx ); - } - } - #if PARALLELIZE_KSOLVE_WITH_CPP11_ASYNC if( 1 < getNumThreads( ) ) cout << "Debug: User wants Ksolve with " << numThreads_ << " threads" << endl; @@ -708,40 +625,12 @@ void Ksolve::reinit( const Eref& e, ProcPtr p ) ////////////////////////////////////////////////////////////// void Ksolve::initProc( const Eref& e, ProcPtr p ) { - // vector< vector< double > > values( xfer_.size() ); - for ( unsigned int i = 0; i < xfer_.size(); ++i ) - { - XferInfo& xf = xfer_[i]; - unsigned int size = xf.xferPoolIdx.size() * xf.xferVoxel.size(); - // values[i].resize( size, 0.0 ); - vector< double > values( size, 0.0 ); - for ( unsigned int j = 0; j < xf.xferVoxel.size(); ++j ) - { - unsigned int vox = xf.xferVoxel[j]; - pools_[vox].xferOut( j, values, xf.xferPoolIdx ); - } - xComptOut()->sendTo( e, xf.ksolve, e.id(), values ); - } - // xComptOut()->sendVec( e, values ); } void Ksolve::initReinit( const Eref& e, ProcPtr p ) { for ( unsigned int i = 0 ; i < pools_.size(); ++i ) pools_[i].reinit( p->dt ); - - for ( unsigned int i = 0; i < xfer_.size(); ++i ) - { - XferInfo& xf = xfer_[i]; - unsigned int size = xf.xferPoolIdx.size() * xf.xferVoxel.size(); - xf.lastValues.assign( size, 0.0 ); - for ( unsigned int j = 0; j < xf.xferVoxel.size(); ++j ) - { - unsigned int vox = xf.xferVoxel[j]; - pools_[ vox ].xferOut( j, xf.lastValues, xf.xferPoolIdx ); - } - xComptOut()->sendTo( e, xf.ksolve, e.id(), xf.lastValues ); - } } /** @@ -919,7 +808,6 @@ void Ksolve::updateVoxelVol( vector< double > vols ) { pools_[i].setVolumeAndDependencies( vols[i] ); } - stoichPtr_->setupCrossSolverReacVols(); updateRateTerms( ~0U ); } } @@ -944,25 +832,5 @@ void Ksolve::print() const cout << "method = " << method_ << ", stoich=" << stoich_.path() <<endl; cout << "dsolve = " << dsolve_.path() << endl; cout << "compartment = " << compartment_.path() << endl; - cout << "xfer summary: numxfer = " << xfer_.size() << "\n"; - for ( unsigned int i = 0; i < xfer_.size(); ++i ) - { - cout << "xfer_[" << i << "] numValues=" << - xfer_[i].values.size() << - ", xferPoolIdx.size = " << xfer_[i].xferPoolIdx.size() << - ", xferVoxel.size = " << xfer_[i].xferVoxel.size() << endl; - } - cout << "xfer details:\n"; - for ( unsigned int i = 0; i < xfer_.size(); ++i ) - { - cout << "xfer_[" << i << "] xferPoolIdx=\n"; - const vector< unsigned int>& xi = xfer_[i].xferPoolIdx; - for ( unsigned int j = 0; j << xi.size(); ++j ) - cout << " " << xi[j]; - cout << "\nxfer_[" << i << "] xferVoxel=\n"; - const vector< unsigned int>& xv = xfer_[i].xferVoxel; - for ( unsigned int j = 0; j << xv.size(); ++j ) - cout << " " << xv[j]; - } } diff --git a/ksolve/Ksolve.h b/ksolve/Ksolve.h index 5e2153bee804a5fa10e812ff0c6bcfd4561b14f5..c7f4af8a378b0764fa17a26b9d731c9e12fb2daa 100644 --- a/ksolve/Ksolve.h +++ b/ksolve/Ksolve.h @@ -126,30 +126,11 @@ public: */ void updateRateTerms( unsigned int index ); - ////////////////////////////////////////////////////////////////// - // Functions for cross-compartment transfer - ////////////////////////////////////////////////////////////////// - void setupXfer( Id myKsolve, Id otherKsolve, - unsigned int numProxyMols, - const vector< VoxelJunction >& vj ); - - void assignXferIndex( unsigned int numProxyMols, - unsigned int xferCompt, - const vector< vector< unsigned int > >& voxy ); - - void assignXferVoxels( unsigned int xferCompt ); - - unsigned int assignProxyPools( const map< Id, vector< Id > >& xr, - Id myKsolve, Id otherKsolve, Id otherComptId ); - - void buildCrossReacVolScaling( Id otherKsolve, - const vector< VoxelJunction >& vj ); ////////////////////////////////////////////////////////////////// // for debugging void print() const; ////////////////////////////////////////////////////////////////// - static SrcFinfo2< Id, vector< double > >* xComptOut(); static const Cinfo* initCinfo(); private: diff --git a/ksolve/Stoich.cpp b/ksolve/Stoich.cpp index a94dfff9d6143a8e9b19d2a2080d8ef19fa7bd86..458151401e2c2a0bcb36a9f04671e1e3b3947891 100644 --- a/ksolve/Stoich.cpp +++ b/ksolve/Stoich.cpp @@ -141,7 +141,7 @@ const Cinfo* Stoich::initCinfo() "matrixEntry", "The non-zero matrix entries in the sparse matrix. Their" "column indices are in a separate vector and the row" - "informatino in a third", + "information in a third", &Stoich::getMatrixEntry ); // Stuff here for getting Stoichiometry matrix to manipulate in @@ -195,22 +195,6 @@ const Cinfo* Stoich::initCinfo() "Restore all zombies to their native state", new OpFunc0< Stoich >( &Stoich::unZombifyModel ) ); - static DestFinfo buildXreacs( "buildXreacs", - "Build cross-reaction terms between current stoich and " - "argument. This function scans the voxels at which there are " - "junctions between different compartments, and orchestrates " - "set up of interfaces between the Ksolves that implement " - "the X reacs at those junctions. ", - new EpFunc1< Stoich, Id >( &Stoich::buildXreacs ) - ); - - static DestFinfo filterXreacs( "filterXreacs", - "Filter cross-reaction terms on current stoich" - "This function clears out absent rate terms that would " - "otherwise try to compute cross reactions where the " - "junctions are not present. ", - new OpFunc0< Stoich >( &Stoich::filterXreacs ) - ); static DestFinfo scaleBufsAndRates( "scaleBufsAndRates", "Args: voxel#, volRatio\n" "Handles requests for runtime volume changes in the specified " @@ -246,8 +230,6 @@ const Cinfo* Stoich::initCinfo() &proxyPools, // ReadOnlyLookupValue &status, // ReadOnlyLookupValue &unzombify, // DestFinfo - &buildXreacs, // DestFinfo - &filterXreacs, // DestFinfo &scaleBufsAndRates, // DestFinfo }; @@ -405,7 +387,6 @@ void Stoich::setElist( const Eref& e, const vector< ObjId >& elist ) status_ = 16; return; } - locateOffSolverReacs( compartment_, temp ); // allocateObjMap( temp ); allocateModel( temp ); @@ -426,55 +407,10 @@ void Stoich::setElist( const Eref& e, const vector< ObjId >& elist ) if ( kinterface_ ) { kinterface_->setDsolve( dsolve_ ); - // kinterface_->setupCrossSolverReacs( offSolverPoolVec_ ); - kinterface_->setupCrossSolverReacVols( subComptVec_, prdComptVec_); kinterface_->updateRateTerms(); } } -void Stoich::setupCrossSolverReacVols() const -{ - kinterface_->setupCrossSolverReacVols( subComptVec_, prdComptVec_); -} - -/* -// Utility function for getting inputs to a single Function object. -static vector< ObjId > inputsToFunc( Id func ) -{ - static const Finfo* inputFinfo = - Cinfo::find( "Variable" )->findFinfo("input"); - static const DestFinfo* df = dynamic_cast< const DestFinfo* >( inputFinfo ); - assert( df ); - static FuncId fid = df->funcId(); - assert( func.element()->cinfo()->isA( "Function" ); - unsigned int numInputs = Field< unsigned int >::get( func, "numVars" ); - Id varId( func.value() ); - vector< ObjId > caller; - varId.element()->getInputMsgs( caller, fid ); - cout << "NUM Inputs = " << numInputs << ", numObjId = " << caller.size() << endl; - return caller; -} - -// routine to get all inputs (pool Ids) to Pool Functions -void Stoich::inputsToPoolFuncs( - vector< pair< Id, vector< unsigned int> >& ret ) const -{ - ret.clear(); - vector< Id >::iterator i; - vector< ObjId >::iterator j; - for ( i = poolFuncVec_.begin(); i != poolFuncVec_.end(); ++i ) - vector< ObjId > ovec = inputsToFunc( *i ); - vector< unsigned int > poolIndex; - poolIndex.reserve( ovec.size() ); - for ( j = ovec.begin(); j != ovec.end(); ++j ) { - unsigned int poolIndex = - poolIndex.push_back( convertIdToPoolIndex( j->id ) ); - } - ret.push_back( pair( *i, poolIndex) ); - return ret; -} -*/ - ////////////////////////////////////////////////////////////////////// string Stoich::getPath( const Eref& e ) const @@ -1276,48 +1212,6 @@ const KinSparseMatrix& Stoich::getStoichiometryMatrix() const return N_; } -void Stoich::buildXreacs( const Eref& e, Id otherStoich ) -{ - if ( status_ == 0 ) - kinterface_->setupCrossSolverReacs( offSolverPoolMap_,otherStoich); -} - -void Stoich::filterXreacs() -{ - if ( status_ == 0 ) { - kinterface_->filterCrossRateTerms( offSolverReacVec_, offSolverReacCompts_ ); - kinterface_->filterCrossRateTerms( offSolverEnzVec_, offSolverEnzCompts_ ); - kinterface_->filterCrossRateTerms( offSolverMMenzVec_, offSolverMMenzCompts_ ); - } -} - -/* -void Stoich::comptsOnCrossReacTerms( vector< pair< Id, Id > >& xr ) const -{ - xr.clear(); - assert( offSolverReacVec_.size() == offSolverReacCompts_.size() ); - assert( offSolverEnzVec_.size() == offSolverEnzCompts_.size() ); - assert( offSolverMMenzVec_.size() == offSolverMMenzCompts_.size() ); - unsigned int numOffSolverRates = getNumRates() - getNumCoreRates(); - if ( numOffSolverRates == 0 ) - return; - unsigned int k = getNumCoreRates(); - unsigned int j = 0; - - for ( unsigned int i = 0; i < offSolverReacs_.size() ; ++i ) { - if ( i+1 < offSolverReacs_.size() ) - j = convertIdToReacIndex( offSolverReacs_[i+1] ); - else - j = rates_.size(); - while ( k < j ) { - xr.push_back( offSolverReacCompts_[i] ); - k++; - } - } - assert( xr.size() == numXrates ); -} -*/ - ////////////////////////////////////////////////////////////// // Model zombification functions ////////////////////////////////////////////////////////////// @@ -1334,31 +1228,6 @@ static Id findFuncMsgSrc( Id pa, const string& msg ) return ret[0]; } return Id(); // failure - - - /* - cout << "fundFuncMsgSrc:: convert to DestFinfo\n"; - const DestFinfo *df = dynamic_cast< const DestFinfo* >( finfo ); - if ( !df ) return Id(); - FuncId fid = df->getFid(); - - cout << "fundFuncMsgSrc:: findCaller\n"; - ObjId caller = pa.element()->findCaller( fid ); - if ( caller.bad() ) - return Id(); - cout << "Found Func for " << pa.path() << " on " << msg << endl; - cout << "Func was " << caller.path() << endl; - return caller.id; - */ - - /* - vector< Id > kids = Neutral::getChildren( pa.eref() ); - for ( vector< Id >::iterator i = kids.begin(); i != kids.end(); ++i ) - if ( i->element()->cinfo()->isA( "Function" ) ) - return( *i ); - return Id(); - */ - } Id Stoich::zombifyPoolFuncWithScaling( Id pool ) diff --git a/ksolve/Stoich.h b/ksolve/Stoich.h index ea5177093e7b18a99f858df0614f3e270cf98986..bea3d067f1b560863ac02f8240841274c1e75af4 100644 --- a/ksolve/Stoich.h +++ b/ksolve/Stoich.h @@ -198,39 +198,9 @@ class Stoich */ void convertRatesToStochasticForm(); - /** - * Builds cross-reaction terms between current stoich and - * otherStoich. The function scans the voxels at which there - * are jucntions between different compartments, and orchestrates - * setup of interfaces between the Ksolves that implement the - * cross-reactions at these junctions. - */ - void buildXreacs( const Eref& e, Id otherStoich ); - - void filterXreacs(); - /// Used to handle run-time size updates for spines. void scaleBufsAndRates( unsigned int index, double volScale ); - /** - * Expands out list of compartment mappings of proxy reactions to - * the appropriate entries on the rates_vector. - void comptsOnCrossReacTerms( vector< pair< Id, Id > >& xr ) const; - */ - - /** - * Used to set up and update all cross solver reac terms and - * their rates, if there has been a change in volumes. Should - * also work if there has been a change in voxelization. - */ - void setupCrossSolverReacVols() const; - - /** - * Finds all the input molecules contributing to any of the - * Function cases: poolFunc, incrementFunc or reacFunc - void inputsToPoolFuncs( - vector< pair< Id, vector< unsigned int > > >& ret ) const; - */ ////////////////////////////////////////////////////////////////// // Zombification functions. ////////////////////////////////////////////////////////////////// diff --git a/ksolve/VoxelPools.cpp b/ksolve/VoxelPools.cpp index 88405e72994e7f1196152349a8904d2adc13e391..64b07be22f8ab7c16761881de85461b5f606433a 100644 --- a/ksolve/VoxelPools.cpp +++ b/ksolve/VoxelPools.cpp @@ -371,60 +371,7 @@ void VoxelPools::print() const void VoxelPools::setVolumeAndDependencies( double vol ) { VoxelPoolsBase::setVolumeAndDependencies( vol ); - stoichPtr_->setupCrossSolverReacVols(); updateAllRateTerms( stoichPtr_->getRateTerms(), stoichPtr_->getNumCoreRates() ); } - -//////////////////////////////////////////////////////////// -#if 0 -/** - * Zeroes out rate terms that are involved in cross-reactions that - * are not present on current voxel. - */ -void VoxelPools::filterCrossRateTerms( - const vector< pair< Id, Id > >& - offSolverReacCompts ) -{ - /* -From VoxelPoolsBase:proxyPoolVoxels[comptIndex][#] we know -if specified compt has local proxies. - Note that compt is identified by an index, and actually looks up - the Ksolve. -From Ksolve::compartment_ we know which compartment a given ksolve belongs - in -From Ksolve::xfer_[otherKsolveIndex].ksolve we have the id of the other - Ksolves. -From Stoich::offSolverReacCompts_ which is pair< Id, Id > we have the - ids of the _compartments_ feeding into the specified rateTerms. - -Somewhere I need to make a map of compts to comptIndex. - -The ordering of the xfer vector is simply by the order of the script call -for buildXfer. - -This has become too ugly -Skip the proxyPoolVoxels info, or use the comptIndex here itself to -build the table. -comptIndex looks up xfer which holds the Ksolve Id. From that we can -get the compt id. All this relies on this mapping being correct. -Or I should pass in the compt when I build it. - -OK, now we have VoxelPoolsBase::proxyPoolCompts_ vector to match the -comptIndex. - -*/ - unsigned int numCoreRates = stoichPtr_->getNumCoreRates(); - for ( unsigned int i = 0; i < offSolverReacCompts.size(); ++i ) { - const pair< Id, Id >& p = offSolverReacCompts[i]; - if ( !isVoxelJunctionPresent( p.first, p.second) ) { - unsigned int k = i + numCoreRates; - assert( k < rates_.size() ); - if ( rates_[k] ) - delete rates_[k]; - rates_[k] = new ExternReac; - } - } -} -#endif diff --git a/ksolve/VoxelPools.h b/ksolve/VoxelPools.h index 05129e361ddcecc1a6e3b4b1db56fc6699f2721b..bd1e5ec5003e23d33699723d04ed21340791458a 100644 --- a/ksolve/VoxelPools.h +++ b/ksolve/VoxelPools.h @@ -95,11 +95,6 @@ public: void updateReacVelocities( const double* s, vector< double >& v ) const; - /** - * Changes cross rate terms to zero if there is no junction - void filterCrossRateTerms( const vector< pair< Id, Id > >& vec ); - */ - /// Used for debugging. void print() const; private: diff --git a/ksolve/ZombiePoolInterface.cpp b/ksolve/ZombiePoolInterface.cpp index 20fb4895bdc290fbc59187445218166e93a28688..b75254d23441f59160fc54e4ca62993adcf8bd95 100644 --- a/ksolve/ZombiePoolInterface.cpp +++ b/ksolve/ZombiePoolInterface.cpp @@ -34,306 +34,12 @@ ZombiePoolInterface::ZombiePoolInterface() isBuilt_( false ) {;} -////////////////////////////////////////////////////////////////////////// -// cross-compartment reaction stuff. -////////////////////////////////////////////////////////////////////////// -// void ZombiePoolInterface::xComptIn( const Eref& e, const ObjId& src, -// vector< double > values ) -void ZombiePoolInterface::xComptIn( const Eref& e, Id srcZombiePoolInterface, - vector< double > values ) -{ - // Identify the xfer_ that maps to the srcZombiePoolInterface. Assume only a small - // number of them, otherwise we should use a map. - unsigned int comptIdx ; - for ( comptIdx = 0 ; comptIdx < xfer_.size(); ++comptIdx ) { - if ( xfer_[comptIdx].ksolve == srcZombiePoolInterface ) break; - } - assert( comptIdx != xfer_.size() ); - XferInfo& xf = xfer_[comptIdx]; - // assert( values.size() == xf.values.size() ); - xf.values = values; -// xfer_[comptIdx].lastValues = values; -} +void ZombiePoolInterface::updateJunctions( double dt ) +{;} +void ZombiePoolInterface::setPrev() +{;} ///////////////////////////////////////////////////////////////////// -// Functions for setup of cross-compartment transfer. -///////////////////////////////////////////////////////////////////// -/** - * Figures out which voxels are involved in cross-compt reactions. Stores - * in the appropriate xfer_ entry. - */ -void ZombiePoolInterface::assignXferVoxels( unsigned int xferCompt ) -{ - assert( xferCompt < xfer_.size() ); - XferInfo& xf = xfer_[xferCompt]; - for ( unsigned int i = 0; i < getNumLocalVoxels(); ++i ) { - if ( pools(i)->hasXfer( xferCompt ) ) - xf.xferVoxel.push_back( i ); - } - xf.values.resize( xf.xferVoxel.size() * xf.xferPoolIdx.size(), 0 ); - xf.lastValues.resize( xf.xferVoxel.size() * xf.xferPoolIdx.size(), 0 ); - xf.subzero.resize( xf.xferVoxel.size() * xf.xferPoolIdx.size(), 0 ); -} - -/** - * Figures out indexing of the array of transferred pool n's used to fill - * out proxies on each timestep. - */ -void ZombiePoolInterface::assignXferIndex( unsigned int numProxyMols, - unsigned int xferCompt, - const vector< vector< unsigned int > >& voxy ) -{ - unsigned int idx = 0; - for ( unsigned int i = 0; i < voxy.size(); ++i ) { - const vector< unsigned int >& rpv = voxy[i]; - if ( rpv.size() > 0) { // There would be a transfer here - for ( vector< unsigned int >::const_iterator - j = rpv.begin(); j != rpv.end(); ++j ) { - pools(*j)->addProxyTransferIndex( xferCompt, idx ); - } - idx += numProxyMols; - } - } -} - -/** - * This function sets up the information about the pool transfer for - * cross-compartment reactions. It consolidates the transfer into a - * distinct vector for each direction of the transfer between each coupled - * pair of ZombiePoolInterfaces. - * This one call sets up the information about transfer on both sides - * of the junction(s) between current ZombiePoolInterface and otherZombiePoolInterface. - * - * VoxelJunction refers to a specific junction between compartments. - * It has the following relevant fields: - * first, second: VoxelIndex for the first and second compartments. - * firstVol, secondVol: VoxelVolume for the first and second compartments. - */ -void ZombiePoolInterface::setupXfer( Id myZombiePoolInterface, Id otherZombiePoolInterface, - unsigned int numProxyMols, const vector< VoxelJunction >& vj ) -{ - const ChemCompt *myCompt = reinterpret_cast< const ChemCompt* >( - compartment_.eref().data() ); - ZombiePoolInterface* otherZombiePoolInterfacePtr = - reinterpret_cast< ZombiePoolInterface* >( - otherZombiePoolInterface.eref().data() ); - const ChemCompt *otherCompt = reinterpret_cast< const ChemCompt* >( - otherZombiePoolInterfacePtr->compartment_.eref().data() ); - // Use this so we can figure out what the other side will send. - vector< vector< unsigned int > > proxyVoxy( myCompt->getNumEntries() ); - vector< vector< unsigned int > > reverseProxyVoxy( otherCompt->getNumEntries() ); - assert( xfer_.size() > 0 && otherZombiePoolInterfacePtr->xfer_.size() > 0 ); - unsigned int myZombiePoolInterfaceIndex = xfer_.size() -1; - unsigned int otherZombiePoolInterfaceIndex = otherZombiePoolInterfacePtr->xfer_.size() -1; - for ( unsigned int i = 0; i < vj.size(); ++i ) { - unsigned int j = vj[i].first; - assert( j < getNumLocalVoxels() ); // Check voxel indices. - proxyVoxy[j].push_back( vj[i].second ); - pools(j)->addProxyVoxy( myZombiePoolInterfaceIndex, - otherZombiePoolInterfacePtr->compartment_, vj[i].second); - unsigned int k = vj[i].second; - assert( k < otherCompt->getNumEntries() ); - reverseProxyVoxy[k].push_back( vj[i].first ); - otherZombiePoolInterfacePtr->pools(k)->addProxyVoxy( - otherZombiePoolInterfaceIndex, compartment_, vj[i].first ); - } - - // Build the indexing for the data values to transfer on each timestep - assignXferIndex( numProxyMols, myZombiePoolInterfaceIndex, reverseProxyVoxy ); - otherZombiePoolInterfacePtr->assignXferIndex( - numProxyMols, otherZombiePoolInterfaceIndex, proxyVoxy ); - // Figure out which voxels participate in data transfer. - assignXferVoxels( myZombiePoolInterfaceIndex ); - otherZombiePoolInterfacePtr->assignXferVoxels( otherZombiePoolInterfaceIndex ); -} - - -/** - * Builds up the list of proxy pools on either side of the junction, - * and assigns to the XferInfo data structures for use during runtime. - */ -unsigned int ZombiePoolInterface::assignProxyPools( const map< Id, vector< Id > >& xr, - Id myZombiePoolInterface, Id otherZombiePoolInterface, Id otherComptId ) -{ - map< Id, vector< Id > >::const_iterator i = xr.find( otherComptId ); - vector< Id > proxyMols; - if ( i != xr.end() ) - proxyMols = i->second; - ZombiePoolInterface* otherZombiePoolInterfacePtr = - reinterpret_cast< ZombiePoolInterface* >( - otherZombiePoolInterface.eref().data() ); - - vector< Id > otherProxies = LookupField< Id, vector< Id > >::get( - otherZombiePoolInterfacePtr->stoich_, "proxyPools", stoich_ ); - - proxyMols.insert( proxyMols.end(), - otherProxies.begin(), otherProxies.end() ); - // if ( proxyMols.size() == 0 ) - // return 0; - sort( proxyMols.begin(), proxyMols.end() ); - xfer_.push_back( XferInfo( otherZombiePoolInterface ) ); - - otherZombiePoolInterfacePtr->xfer_.push_back( XferInfo( myZombiePoolInterface ) ); - vector< unsigned int >& xfi = xfer_.back().xferPoolIdx; - vector< unsigned int >& oxfi = otherZombiePoolInterfacePtr->xfer_.back().xferPoolIdx; - xfi.resize( proxyMols.size() ); - oxfi.resize( proxyMols.size() ); - for ( unsigned int i = 0; i < xfi.size(); ++i ) { - xfi[i] = getPoolIndex( proxyMols[i].eref() ); - oxfi[i] = otherZombiePoolInterfacePtr->getPoolIndex( - proxyMols[i].eref() ); - } - return proxyMols.size(); -} - - -// This function cleans out the RateTerms of cross reactions that -// don't have anything to connect to. -// It should be called after all cross reacs have been assigned. -void ZombiePoolInterface::filterCrossRateTerms( const vector< Id >& xreacs, - const vector< pair< Id, Id > >& xrt ) -{ - for (unsigned int i = 0; i < getNumLocalVoxels(); ++i ) { - pools(i)->filterCrossRateTerms( xreacs, xrt ); - } -} - -/** - * This function builds cross-solver reaction calculations. For the - * specified pair of stoichs (this->stoich_, otherStoich) it identifies - * interacting molecules, finds where the junctions are, sets up the - * info to build the data transfer vector, and sets up the transfer - * itself. - */ -void ZombiePoolInterface::setupCrossSolverReacs( const map< Id, vector< Id > >& xr, - Id otherStoich ) -{ - const ChemCompt *myCompt = reinterpret_cast< const ChemCompt* >( - compartment_.eref().data() ); - Id otherComptId = Field< Id >::get( otherStoich, "compartment" ); - Id myZombiePoolInterface = Field< Id >::get( stoich_, "ksolve" ); - if ( myZombiePoolInterface == Id() ) - return; - Id otherZombiePoolInterface = Field< Id >::get( otherStoich, "ksolve" ); - if ( otherZombiePoolInterface == Id() ) - return; - - // Establish which molecules will be exchanged. - unsigned int numPools = assignProxyPools( xr, myZombiePoolInterface, otherZombiePoolInterface, - otherComptId ); - if ( numPools == 0 ) return; - - // Then, figure out which voxels do the exchange. - // Note that vj has a list of pairs of voxels on either side of a - // junction. If one voxel on self touches 5 voxels on other, then - // there will be five entries in vj for this contact. - // If one voxel on self touches two different compartments, then - // a distinct vj vector must be built for those contacts. - const ChemCompt *otherCompt = reinterpret_cast< const ChemCompt* >( - otherComptId.eref().data() ); - vector< VoxelJunction > vj; - myCompt->matchMeshEntries( otherCompt, vj ); - if ( vj.size() == 0 ) - return; - - // This function sets up the information about the pool transfer on - // both sides. - setupXfer( myZombiePoolInterface, otherZombiePoolInterface, numPools, vj ); - - /// This sets up the volume scaling from cross reac terms - // Deprecated. Handled by setupCrossSolverReacVols. - // buildCrossReacVolScaling( otherZombiePoolInterface, vj ); - - // Here we set up the messaging. - Shell *shell = reinterpret_cast< Shell* >( Id().eref().data() ); - shell->doAddMsg( "Single", myZombiePoolInterface, "xCompt", otherZombiePoolInterface, "xCompt" ); -} - -/** - * This fills the vols vector with the volume of the abutting - * voxel on compt. If there are no abutting voxels on a given - * voxel then that entry of the vols vector is filled with a zero. - * There is exactly one vols entry for each voxel of the local compt. - */ -void ZombiePoolInterface::matchJunctionVols( vector< double >& vols, Id otherComptId ) - const -{ - vols.resize( getNumLocalVoxels() ); - for ( unsigned int i = 0; i < vols.size(); ++i ) - vols[i] = volume(i); - if ( otherComptId == compartment_ ) { - // This may legitimately happen if the substrate or product is - // on the local compartment. - // cout << "Warning: ZombiePoolInterface::matchJunctionVols: self compt.\n"; - return; - } - const ChemCompt *myCompt = reinterpret_cast< const ChemCompt* >( - compartment_.eref().data() ); - const ChemCompt *otherCompt = reinterpret_cast< const ChemCompt* >( - otherComptId.eref().data() ); - vector< VoxelJunction > vj; - myCompt->matchMeshEntries( otherCompt, vj ); - if ( vj.size() == 0 ) - return; - for ( vector< VoxelJunction >::const_iterator - i = vj.begin(); i != vj.end(); ++i ) { - assert( i->first < vols.size() ); - /* - if ( !doubleEq( vols[ i->first ], 0.0 ) ) - cout << "Warning: ZombiePoolInterface::matchJuntionVols: repeated voxel\n"; - */ - vols[ i->first ] = i->secondVol; - } -} - -/** - * This function builds cross-solver reaction volume scaling. - */ -void ZombiePoolInterface::setupCrossSolverReacVols( - const vector< vector< Id > >& subCompts, - const vector< vector< Id > >& prdCompts ) -{ - map< Id, vector< double > > comptVolMap; - const Stoich* stoichPtr = reinterpret_cast< const Stoich* >( - stoich_.eref().data() ); - unsigned int numOffSolverReacs = - stoichPtr->getNumRates() - stoichPtr->getNumCoreRates(); - assert( subCompts.size() == numOffSolverReacs ); - assert( prdCompts.size() == numOffSolverReacs ); - for ( unsigned int i = 0 ; i < getNumLocalVoxels(); ++i ) - pools(i)->resetXreacScale( numOffSolverReacs ); - for( unsigned int i = 0; i < numOffSolverReacs; ++i ) { - for ( unsigned int j = 0; j < subCompts[i].size(); ++j ) { - map< Id, vector< double > >::iterator cvi; - vector< double > vols; - cvi = comptVolMap.find( subCompts[i][j] ); - if ( cvi == comptVolMap.end() ) { - matchJunctionVols( vols, subCompts[i][j] ); - comptVolMap[subCompts[i][j]] = vols; - } else { - vols = cvi->second; - } - assert( vols.size() == getNumLocalVoxels() ); - for ( unsigned int k = 0; k < vols.size(); ++k ) - pools(k)->forwardReacVolumeFactor( i, vols[k] ); - } - - for ( unsigned int j = 0; j < prdCompts[i].size(); ++j ) { - map< Id, vector< double > >::iterator cvi; - vector< double > vols; - cvi = comptVolMap.find( prdCompts[i][j] ); - if ( cvi == comptVolMap.end() ) { - matchJunctionVols( vols, prdCompts[i][j] ); - comptVolMap[prdCompts[i][j]] = vols; - } else { - vols = cvi->second; - } - assert( vols.size() == getNumLocalVoxels() ); - for ( unsigned int k = 0; k < vols.size(); ++k ) - pools(k)->backwardReacVolumeFactor( i, vols[k] ); - } - } -} Id ZombiePoolInterface::getCompartment() const { diff --git a/ksolve/ZombiePoolInterface.h b/ksolve/ZombiePoolInterface.h index 10104fd2f5a54edde76928326d6e2215f6a23e58..fe5bb1d8d98e6b0dd93d8d1b888a4b6bf2c11b13 100644 --- a/ksolve/ZombiePoolInterface.h +++ b/ksolve/ZombiePoolInterface.h @@ -96,16 +96,10 @@ class ZombiePoolInterface virtual void setCompartment( Id compartment ); Id getCompartment() const; - /// Sets up cross-solver reactions. - 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< Id >& xreacs, - const vector< pair< Id, Id > >& xrt ); + /// 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. @@ -116,24 +110,6 @@ class ZombiePoolInterface /// Return pool index, using Stoich ptr to do lookup. virtual unsigned int getPoolIndex( const Eref& er ) const = 0; - ////////////////////////////////////////////////////////////// - // Utility functions for Cross-compt reaction setup. - ////////////////////////////////////////////////////////////// - void xComptIn( const Eref& e, Id srcZombiePoolInterface, - vector< double > values ); - // void xComptOut( const Eref& e ); - void assignXferVoxels( unsigned int xferCompt ); - void assignXferIndex( unsigned int numProxyMols, - unsigned int xferCompt, - const vector< vector< unsigned int > >& voxy ); - void setupXfer( Id myZombiePoolInterface, - Id otherZombiePoolInterface, - unsigned int numProxyMols, const vector< VoxelJunction >& vj ); - unsigned int assignProxyPools( const map< Id, vector< Id > >& xr, - Id myZombiePoolInterface, Id otherZombiePoolInterface, - Id otherComptId ); - void matchJunctionVols( vector< double >& vols, Id otherComptId ) - const; ////////////////////////////////////////////////////////////// protected: @@ -146,12 +122,6 @@ class ZombiePoolInterface /// Id of Chem compartment used to figure out volumes of voxels. Id compartment_; - /** - * All the data transfer information from current to other solvers. - * xfer_[otherKsolveIndex] - */ - vector< XferInfo > xfer_; - /// Flag: True when solver setup has been completed. bool isBuilt_; }; diff --git a/mesh/CMakeLists.txt b/mesh/CMakeLists.txt index 6046fc568dd566e42da372e50e3338ac999157b8..a04e31ddec7611a56febe8af1f51661d86ea6bcd 100644 --- a/mesh/CMakeLists.txt +++ b/mesh/CMakeLists.txt @@ -13,5 +13,6 @@ add_library(mesh SpineEntry.cpp SpineMesh.cpp PsdMesh.cpp + EndoMesh.cpp testMesh.cpp ) diff --git a/mesh/ChemCompt.cpp b/mesh/ChemCompt.cpp index 32ae58e2432ca26bcb67fb99636639a14ba3ca3a..1d2b3f7f289a3c1bd4399bf32115170612540198 100644 --- a/mesh/ChemCompt.cpp +++ b/mesh/ChemCompt.cpp @@ -89,6 +89,18 @@ const Cinfo* ChemCompt::initCinfo() &ChemCompt::getStencilIndex ); + static ValueFinfo< ChemCompt, bool > isMembraneBound( + "isMembraneBound", + "Flag, set to True for meshes where each voxel is membrane " + "bound. \n" + "NeuroMesh and SpineMesh are false. \n" + "CubeMesh, CylMesh, and EndoMesh can be either. If they are " + "membrane bound they can still interact via channels and " + "cross-compartment reactions. ", + &ChemCompt::setIsMembraneBound, + &ChemCompt::getIsMembraneBound + ); + ////////////////////////////////////////////////////////////// // MsgDest Definitions ////////////////////////////////////////////////////////////// @@ -148,6 +160,7 @@ const Cinfo* ChemCompt::initCinfo() &numDimensions, // ReadOnlyValue &stencilRate, // ReadOnlyLookupValue &stencilIndex, // ReadOnlyLookupValue + &isMembraneBound, // Value voxelVolOut(), // SrcFinfo &buildDefaultMesh, // DestFinfo &setVolumeNotRates, // DestFinfo @@ -184,7 +197,8 @@ static const Cinfo* chemMeshCinfo = ChemCompt::initCinfo(); ChemCompt::ChemCompt() : - entry_( this ) + entry_( this ), + isMembraneBound_( false ) { ; } @@ -338,6 +352,16 @@ vector< unsigned int > ChemCompt::getStencilIndex( unsigned int row ) const return this->getNeighbors( row ); } +bool ChemCompt::getIsMembraneBound() const +{ + return isMembraneBound_; +} + +void ChemCompt::setIsMembraneBound( bool v ) +{ + isMembraneBound_ = v; +} + ////////////////////////////////////////////////////////////// // Dest Definitions diff --git a/mesh/ChemCompt.h b/mesh/ChemCompt.h index eb77b965fc883bb585ff2f412ea1ae2646c80cb5..ba909d2e825285d4b1b89f031d29ab94f510680d 100644 --- a/mesh/ChemCompt.h +++ b/mesh/ChemCompt.h @@ -69,6 +69,9 @@ class ChemCompt void setMethod( string method ); string getMethod() const; + bool getIsMembraneBound() const; + void setIsMembraneBound( bool v ); + /** * Function to return the stencil values used in the * diffusion calculations for voxelized compartments. @@ -349,6 +352,15 @@ class ChemCompt * function. */ string method_; + + /** + * Flag, set to True for meshes where each voxel is membrane bound. + * NeuroMesh and SpineMesh are false + * CubeMesh, CylMesh, and EndoMesh can be either. If they are + * membrane bound they can still interact via channels and + * cross-compartment reactions. + */ + bool isMembraneBound_; }; extern SrcFinfo5< diff --git a/mesh/CubeMesh.cpp b/mesh/CubeMesh.cpp index da90d72b5afef9776b522e50cad676be8bc2b8f5..728c69ba095b1176e9d731be2a9f70f69f50c6a6 100644 --- a/mesh/CubeMesh.cpp +++ b/mesh/CubeMesh.cpp @@ -17,6 +17,7 @@ #include "ChemCompt.h" #include "MeshCompt.h" #include "CubeMesh.h" +#include "EndoMesh.h" const unsigned int CubeMesh::EMPTY = ~0; const unsigned int CubeMesh::SURFACE = ~1; @@ -223,13 +224,26 @@ const Cinfo* CubeMesh::initCinfo() &surface, // Value }; + static string doc[] = + { + "Name", "CubeMesh", + "Author", "Upi Bhalla", + "Description", "Chemical compartment with cuboid grid. " + "Defaults to a cube of size 10 microns, with mesh size " + "also 10 microns, so that there is just 1 cubic voxel. " + "These defaults are similar to that of a typical cell. " + "Can be configured to have different x,y,z dimensions and " + "also different dx,dy,dz voxel sizes. ", + }; static Dinfo< CubeMesh > dinfo; static Cinfo cubeMeshCinfo ( "CubeMesh", ChemCompt::initCinfo(), cubeMeshFinfos, sizeof( cubeMeshFinfos ) / sizeof ( Finfo* ), - &dinfo + &dinfo, + doc, + sizeof(doc)/sizeof(string) ); return &cubeMeshCinfo; @@ -252,12 +266,12 @@ CubeMesh::CubeMesh() x0_( 0.0 ), y0_( 0.0 ), z0_( 0.0 ), - x1_( 1.0 ), - y1_( 1.0 ), - z1_( 1.0 ), - dx_( 1.0 ), - dy_( 1.0 ), - dz_( 1.0 ), + x1_( 10e-6 ), + y1_( 10e-6 ), + z1_( 10e-6 ), + dx_( 10e-6 ), + dy_( 10e-6 ), + dz_( 10e-6 ), nx_( 1 ), ny_( 1 ), nz_( 1 ), @@ -887,9 +901,16 @@ void CubeMesh::innerSetNumEntries( unsigned int n ) cout << "Warning: CubeMesh::innerSetNumEntries is readonly.\n"; } +// We assume this is linear diffusion for now. Fails for 2 or 3-D diffusion vector< unsigned int > CubeMesh::getParentVoxel() const { - static vector< unsigned int > ret; + unsigned int numEntries = innerGetNumEntries(); + 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; } @@ -921,14 +942,27 @@ const vector< double >& CubeMesh::vGetVoxelMidpoint() const const vector< double >& CubeMesh::getVoxelArea() const { static vector< double > area; - assert( 0 ); // Not yet operational + if ( nx_ * ny_ == 1 ) + area.resize( nz_, dx_ * dy_ ); + else if ( nx_ * nz_ == 1 ) + area.resize( ny_, dx_ * dz_ ); + else if ( ny_ * nz_ == 1 ) + area.resize( nx_, dy_ * dz_ ); + else + area.resize( nx_, dy_ * dz_ ); // Just put in a number. + assert( area.size() == nx_ * ny_ * nz_ ); return area; } const vector< double >& CubeMesh::getVoxelLength() const { static vector< double > length; - assert( 0 ); // Not yet operational + if ( dx_ > dy_ && dx_ > dz_ ) + length.assign( innerGetNumEntries(), dx_ ); + else if ( dy_ > dz_ ) + length.assign( innerGetNumEntries(), dy_ ); + else + length.assign( innerGetNumEntries(), dz_ ); return length; } @@ -1100,9 +1134,15 @@ void CubeMesh::matchMeshEntries( const ChemCompt* other, cout << "Warning:CubeMesh::matchMeshEntries: cannot yet handle unequal meshes\n"; } */ - } else { - cout << "Warning:CubeMesh::matchMeshEntries: cannot yet handle Neuro or Cyl meshes.\n"; + return; + } + const EndoMesh* em = dynamic_cast< const EndoMesh* >( other ); + if ( em ) { + em->matchMeshEntries( this, ret ); + flipRet( ret ); + return; } + cout << "Warning:CubeMesh::matchMeshEntries: cannot yet handle Neuro or Cyl meshes.\n"; } unsigned int CubeMesh::numDims() const diff --git a/mesh/CylMesh.cpp b/mesh/CylMesh.cpp index ed2c6c4372832ed0ee1033ccaa2c03612f2c14a5..82cd895ffa2fcfdad6bc5aaee9318981c3c9e57b 100644 --- a/mesh/CylMesh.cpp +++ b/mesh/CylMesh.cpp @@ -22,6 +22,7 @@ // #include "NeuroStencil.h" #include "NeuroMesh.h" #include "CylMesh.h" +#include "EndoMesh.h" #include "../utility/numutil.h" const Cinfo* CylMesh::initCinfo() { @@ -131,13 +132,26 @@ const Cinfo* CylMesh::initCinfo() &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 + &dinfo, + doc, + sizeof(doc)/sizeof(string) ); return &cylMeshCinfo; @@ -154,20 +168,20 @@ static const Cinfo* cylMeshCinfo = CylMesh::initCinfo(); ////////////////////////////////////////////////////////////////// CylMesh::CylMesh() : - numEntries_( 1 ), + numEntries_( 100 ), useCaps_( 0 ), isToroid_( false ), x0_( 0.0 ), y0_( 0.0 ), z0_( 0.0 ), - x1_( 1.0 ), + x1_( 100.0e-6 ), y1_( 0.0 ), z1_( 0.0 ), - r0_( 1.0 ), - r1_( 1.0 ), - diffLength_( 1.0 ), + r0_( 1.0e-6 ), + r1_( 1.0e-6 ), + diffLength_( 1.0e-6 ), surfaceGranularity_( 0.1 ), - totLen_( 1.0 ), + totLen_( 100.0e-6 ), rSlope_( 0.0 ), lenSlope_( 0.0 ) { @@ -818,20 +832,27 @@ void CylMesh::matchMeshEntries( const ChemCompt* other, const CylMesh* cyl = dynamic_cast< const CylMesh* >( other ); if ( cyl ) { matchCylMeshEntries( cyl, ret ); - } else { - const CubeMesh* cube = dynamic_cast< const CubeMesh* >( other ); - if ( cube ) { - matchCubeMeshEntries( cube, ret ); - } else { - const NeuroMesh* nm = dynamic_cast< const NeuroMesh* >( other ); - if ( nm ) { - matchNeuroMeshEntries( nm, ret ); - } else { - cout << "Warning:CylMesh::matchMeshEntries: " << - " unknown mesh type\n"; - } - } + return; + } + const EndoMesh* em = dynamic_cast< const EndoMesh* >( other ); + if ( em ) + { + em->matchMeshEntries( this, ret ); + 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"; } // Look for end-to-end diffusion, not sideways for now. diff --git a/mesh/EndoMesh.cpp b/mesh/EndoMesh.cpp new file mode 100644 index 0000000000000000000000000000000000000000..33c13bdbb4c6c7aa9ccb2d6d09e8eb5d05b0307d --- /dev/null +++ b/mesh/EndoMesh.cpp @@ -0,0 +1,464 @@ +/********************************************************************** +** This program is part of 'MOOSE', the +** Messaging Object Oriented Simulation Environment. +** Copyright (C) 2011 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. +**********************************************************************/ + +#include "header.h" +#include "SparseMatrix.h" +#include "ElementValueFinfo.h" +#include "../utility/Vec.h" +#include "Boundary.h" +#include "MeshEntry.h" +// #include "Stencil.h" +#include "ChemCompt.h" +#include "MeshCompt.h" +#include "CubeMesh.h" +#include "CylBase.h" +#include "NeuroNode.h" +// #include "NeuroStencil.h" +#include "NeuroMesh.h" +#include "EndoMesh.h" +#include "../utility/numutil.h" +const Cinfo* EndoMesh::initCinfo() +{ + ////////////////////////////////////////////////////////////// + // Field Definitions + ////////////////////////////////////////////////////////////// + static ElementValueFinfo< EndoMesh, double > rPower( + "rPower", + "Used in rEndo = rScale * pow(surroundVol, rPower)." + "Used to obtain radius of each endo voxel from matching " + "surround voxel. Defaults to 1/3", + &EndoMesh::setRpower, + &EndoMesh::getRpower + ); + static ElementValueFinfo< EndoMesh, double > rScale( + "rScale", + "Used in rEndo = rScale * pow(surroundVol, rPower)." + "Used to obtain radius of each endo voxel from matching " + "surround voxel. Defaults to 0.5", + &EndoMesh::setRscale, + &EndoMesh::getRscale + ); + static ElementValueFinfo< EndoMesh, double > aPower( + "aPower", + "Used in rEndo = aScale * pow(surroundVol, aPower)." + "Used to obtain area of each endo voxel from matching " + "surround voxel. Defaults to 1/2", + &EndoMesh::setApower, + &EndoMesh::getApower + ); + static ElementValueFinfo< EndoMesh, double > aScale( + "aScale", + "Used in rEndo = aScale * pow(surroundVol, aPower)." + "Used to obtain area of each endo voxel from matching " + "surround voxel. Defaults to 0.25", + &EndoMesh::setAscale, + &EndoMesh::getAscale + ); + static ElementValueFinfo< EndoMesh, double > vPower( + "vPower", + "Used in rEndo = vScale * pow(surroundVol, vPower)." + "Used to obtain volume of each endo voxel from matching " + "surround voxel. Defaults to 1.", + &EndoMesh::setVpower, + &EndoMesh::getVpower + ); + static ElementValueFinfo< EndoMesh, double > vScale( + "vScale", + "Used in rEndo = vScale * pow(surroundVol, vPower)." + "Used to obtain volume of each endo voxel from matching " + "surround voxel. Defaults to 0.125", + &EndoMesh::setVscale, + &EndoMesh::getVscale + ); + + static ElementValueFinfo< EndoMesh, ObjId > surround( + "surround", + "ObjId of compartment surrounding current EndoMesh", + &EndoMesh::setSurround, + &EndoMesh::getSurround + ); + + static ElementValueFinfo< EndoMesh, bool > doAxialDiffusion( + "doAxialDiffusion", + "Flag to determine if endoMesh should undertake axial " + "diffusion. Defaults to 'false'. " + "Should only be used within NeuroMesh and CylMesh. " + "Must be assigned before Dsolver is built.", + &EndoMesh::setDoAxialDiffusion, + &EndoMesh::getDoAxialDiffusion + ); + + + ////////////////////////////////////////////////////////////// + // MsgDest Definitions + ////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////// + // Field Elements + ////////////////////////////////////////////////////////////// + + static Finfo* endoMeshFinfos[] = { + &rPower, // Value + &rScale, // Value + &aPower, // Value + &aScale, // Value + &vPower, // Value + &vScale, // Value + &surround, // Value + &doAxialDiffusion, // Value + }; + + static Dinfo< EndoMesh > dinfo; + static Cinfo endoMeshCinfo ( + "EndoMesh", + ChemCompt::initCinfo(), + endoMeshFinfos, + sizeof( endoMeshFinfos ) / sizeof ( Finfo* ), + &dinfo + ); + + return &endoMeshCinfo; +} + +////////////////////////////////////////////////////////////// +// Basic class Definitions +////////////////////////////////////////////////////////////// + +static const Cinfo* endoMeshCinfo = EndoMesh::initCinfo(); + +////////////////////////////////////////////////////////////////// +// Class stuff. +////////////////////////////////////////////////////////////////// +EndoMesh::EndoMesh() + : + parent_( 0 ), + rPower_( 1.0 / 3.0 ), + rScale_( 0.5 ), + aPower_( 0.5 ), + aScale_( 0.25 ), + vPower_( 1.0 ), + vScale_( 0.125 ), + doAxialDiffusion_( false ) +{ + ; +} + +EndoMesh::~EndoMesh() +{ + ; +} + +////////////////////////////////////////////////////////////////// +// Field assignment stuff +////////////////////////////////////////////////////////////////// +// +void EndoMesh::setRpower( const Eref& e, double v ) +{ + rPower_ = v; +} + +double EndoMesh::getRpower( const Eref& e ) const +{ + return rPower_; +} + +void EndoMesh::setRscale( const Eref& e, double v ) +{ + rScale_ = v; +} + +double EndoMesh::getRscale( const Eref& e ) const +{ + return rScale_; +} + +void EndoMesh::setApower( const Eref& e, double v ) +{ + aPower_ = v; +} + +double EndoMesh::getApower( const Eref& e ) const +{ + return aPower_; +} + +void EndoMesh::setAscale( const Eref& e, double v ) +{ + aScale_ = v; +} + +double EndoMesh::getAscale( const Eref& e ) const +{ + return aScale_; +} + +void EndoMesh::setVpower( const Eref& e, double v ) +{ + vPower_ = v; +} + +double EndoMesh::getVpower( const Eref& e ) const +{ + return vPower_; +} + +void EndoMesh::setVscale( const Eref& e, double v ) +{ + vScale_ = v; +} + +double EndoMesh::getVscale( const Eref& e ) const +{ + return vScale_; +} + +void EndoMesh::setSurround( const Eref& e, ObjId v ) +{ + if ( !v.element()->cinfo()->isA( "ChemCompt" ) ) { + cout << "Warning: 'surround' may only be set to an object of class 'ChemCompt'\n"; + cout << v.path() << " is of class " << v.element()->cinfo()->name() << endl; + return; + } + surround_ = v; + parent_ = reinterpret_cast< const MeshCompt* >( v.data() ); +} + +ObjId EndoMesh::getSurround( const Eref& e ) const +{ + return surround_; +} + +void EndoMesh::setDoAxialDiffusion( const Eref& e, bool v ) +{ + doAxialDiffusion_ = v; +} + +bool EndoMesh::getDoAxialDiffusion( const Eref& e ) const +{ + return doAxialDiffusion_; +} + +////////////////////////////////////////////////////////////////// +// FieldElement assignment stuff for MeshEntries +////////////////////////////////////////////////////////////////// + +/// Virtual function to return MeshType of specified entry. +unsigned int EndoMesh::getMeshType( unsigned int fid ) const +{ + return ENDO; +} + +/// Virtual function to return dimensions of specified entry. +unsigned int EndoMesh::getMeshDimensions( unsigned int fid ) const +{ + return 3; +} + +/// Virtual function to return # of spatial dimensions of mesh +unsigned int EndoMesh::innerGetDimensions() const +{ + return 1; +} +/// Virtual function to return volume of mesh Entry. +double EndoMesh::getMeshEntryVolume( unsigned int fid ) const +{ + double vpa = parent_->getMeshEntryVolume( fid ); + return vScale_ * pow( vpa, vPower_ ); +} + +/// Virtual function to return coords of mesh Entry. +/// For Endo mesh, coords are just middle of parent. +vector< double > EndoMesh::getCoordinates( unsigned int fid ) const +{ + vector< double > temp = parent_->getCoordinates( fid ); + vector< double > ret; + if ( temp.size() > 6 ) { + ret.resize( 4 ); + ret[0] = 0.5 * (temp[0] + temp[3] ); + ret[1] = 0.5 * (temp[1] + temp[4] ); + ret[2] = 0.5 * (temp[2] + temp[5] ); + ret[3] = 0; + } + return ret; +} + +/// Virtual function to return diffusion X-section area for each neighbor +/// This applies if we have endo mesh voxels diffusing with each other +vector< double > EndoMesh::getDiffusionArea( unsigned int fid ) const +{ + return vector< double >( parent_->getNumEntries(), 0.0 ); +} + +/// Virtual function to return scale factor for diffusion. 1 here. +/// This applies if we have endo mesh voxels diffusing with each other +vector< double > EndoMesh::getDiffusionScaling( unsigned int fid ) const +{ + return vector< double >( parent_->getNumEntries(), 0.0 ); +} + +/// Virtual function to return volume of mesh Entry, including +/// for diffusively coupled voxels from other solvers. +double EndoMesh::extendedMeshEntryVolume( unsigned int fid ) const +{ + double vpa = parent_->extendedMeshEntryVolume( fid ); + return vScale_ * pow( vpa, vPower_ ); +} + +////////////////////////////////////////////////////////////////// +// Dest funcs +////////////////////////////////////////////////////////////////// + +/// More inherited virtual funcs: request comes in for mesh stats +void EndoMesh::innerHandleRequestMeshStats( const Eref& e, + const SrcFinfo2< unsigned int, vector< double > >* meshStatsFinfo + ) +{ + vector< double > ret( vGetEntireVolume() / parent_->getNumEntries(), 1); + meshStatsFinfo->send( e, 1, ret ); +} + +void EndoMesh::innerHandleNodeInfo( + const Eref& e, + unsigned int numNodes, unsigned int numThreads ) +{ +} +////////////////////////////////////////////////////////////////// + +/** + * Inherited virtual func. Returns number of MeshEntry in array + */ +unsigned int EndoMesh::innerGetNumEntries() const +{ + return parent_->innerGetNumEntries(); +} + +/** + * Inherited virtual func. Dummy, because the EndoMesh just does what its + * parent does. + */ +void EndoMesh::innerSetNumEntries( unsigned int n ) +{ +} + + +void EndoMesh::innerBuildDefaultMesh( const Eref& e, + double volume, unsigned int numEntries ) +{ +} + +/** + * This means the sibling voxel immediately adjacent to it, not the + * voxel surrounding it. Should we wish to do diffusion we would need + * to use the parent voxel of the surround voxel. Otherewise use + * EMTPY_VOXEL == -1U. + */ +vector< unsigned int > EndoMesh::getParentVoxel() const +{ + if ( doAxialDiffusion_ ) + return parent_->getParentVoxel(); + + vector< unsigned int > ret( parent_->innerGetNumEntries(), -1U ); + return ret; +} + +const vector< double >& EndoMesh::vGetVoxelVolume() const +{ + static vector< double > ret; + ret = parent_->vGetVoxelVolume(); + for ( auto i = ret.begin(); i != ret.end(); ++i ) + *i = vScale_ * pow( *i, vPower_ ); + return ret; +} + +const vector< double >& EndoMesh::vGetVoxelMidpoint() const +{ + return parent_->vGetVoxelMidpoint(); +} + +const vector< double >& EndoMesh::getVoxelArea() const +{ + static vector< double > ret; + ret = parent_->vGetVoxelVolume(); + for ( auto i = ret.begin(); i != ret.end(); ++i ) + *i = aScale_ * pow( *i, aPower_ ); + return ret; +} + +const vector< double >& EndoMesh::getVoxelLength() const +{ + static vector< double > ret; + ret = parent_->vGetVoxelVolume(); + for ( auto i = ret.begin(); i != ret.end(); ++i ) + *i = vScale_ * pow( *i, vPower_ ); + return ret; +} + +double EndoMesh::vGetEntireVolume() const +{ + double vol = 0.0; + auto vec = vGetVoxelVolume(); + for ( auto i = vec.begin(); i != vec.end(); ++i ) + vol += *i; + return vol; +} + +bool EndoMesh::vSetVolumeNotRates( double volume ) +{ + return true; // maybe should be false? Unsure +} + +////////////////////////////////////////////////////////////////// +// Utility function for junctions +////////////////////////////////////////////////////////////////// + +void EndoMesh::matchMeshEntries( const ChemCompt* other, + vector< VoxelJunction >& ret ) const +{ + /** + * Consider concentric EndoMeshes in an outer cyl/sphere of radius r_0. + * Radii, from out to in, are r_0, r_1, r_2.... r_innermost. + * Consider that we are on EndoMesh n. + * The diffusion length in each case is 1/2(r_n-1 - r_n+1). + * For the innermost we use r_n+1 = 0 + * The diffusion XA is the area of the EndoMesh. + */ + vector< double > vol = other->vGetVoxelVolume(); + const MeshCompt* mc = static_cast< const MeshCompt* >( other ); + vector< double > len = mc->getVoxelLength(); + assert( vol.size() == len.size() ); + ret.resize( vol.size() ); + vector< double > myVol = vGetVoxelVolume(); + assert( myVol.size() == vol.size() ); + vector< double > myArea = getVoxelArea(); + assert( myArea.size() == vol.size() ); + + for ( unsigned int i = 0; i < vol.size(); ++i ) { + double rSurround = sqrt( vol[i] / (len[i] * PI ) ); + ret[i].first = ret[i].second = i; + ret[i].firstVol = myVol[i]; + ret[i].secondVol = vol[i]; + + // For now we don't consider internal EndoMeshes. + ret[i].diffScale = 2.0 * myArea[i] / rSurround; + } +} + +// This function returns the index. +double EndoMesh::nearest( double x, double y, double z, + unsigned int& index ) const +{ + return parent_->nearest( x, y, z, index ); +} + +// This function returns the index. +void EndoMesh::indexToSpace( unsigned int index, + double &x, double &y, double &z ) const +{ + parent_->indexToSpace( index, x, y, z ); +} diff --git a/mesh/EndoMesh.h b/mesh/EndoMesh.h new file mode 100644 index 0000000000000000000000000000000000000000..5567bda72de18894cce29902d82d450711bc29b7 --- /dev/null +++ b/mesh/EndoMesh.h @@ -0,0 +1,150 @@ +/********************************************************************** +** This program is part of 'MOOSE', the +** Messaging Object Oriented Simulation Environment. +** Copyright (C) 2011 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. +**********************************************************************/ + +#ifndef _ENDO_MESH_H +#define _ENDO_MESH_H + +/** + * The EndoMesh is a cellular compartment entirely contained within another + * compartment. It supports diffusion only with its immediate surround, + * of which there can only be one, and with its immediate interior, which + * can be one or more + * EndoMeshes. Each voxel in the EndoMesh is well mixed, and does not + * exchange with any other voxels. + * Typically used in modelling endosomes, ER, mitochondria, and other + * organelles. + */ +class EndoMesh: public MeshCompt +{ + public: + EndoMesh(); + ~EndoMesh(); + ////////////////////////////////////////////////////////////////// + // Field assignment stuff + ////////////////////////////////////////////////////////////////// + void setRpower( const Eref& e, double v ); + double getRpower( const Eref& e ) const; + void setRscale( const Eref& e, double v ); + double getRscale( const Eref& e ) const; + void setApower( const Eref& e, double v ); + double getApower( const Eref& e ) const; + void setAscale( const Eref& e, double v ); + double getAscale( const Eref& e ) const; + void setVpower( const Eref& e, double v ); + double getVpower( const Eref& e ) const; + void setVscale( const Eref& e, double v ); + double getVscale( const Eref& e ) const; + void setSurround( const Eref& e, ObjId v ); + ObjId getSurround( const Eref& e ) const; + void setDoAxialDiffusion( const Eref& e, bool v ); + bool getDoAxialDiffusion( const Eref& e ) const; + + ////////////////////////////////////////////////////////////////// + // FieldElement assignment stuff for MeshEntries + ////////////////////////////////////////////////////////////////// + + /// Virtual function to return MeshType of specified entry. + unsigned int getMeshType( unsigned int fid ) const; + /// Virtual function to return dimensions of specified entry. + unsigned int getMeshDimensions( unsigned int fid ) const; + unsigned int innerGetDimensions() const; + /// Virtual function to return volume of mesh Entry. + double getMeshEntryVolume( unsigned int fid ) const; + /// Virtual function to return coords of mesh Entry. + vector< double > getCoordinates( unsigned int fid ) const; + /// Virtual function to return diffusion X-section area + vector< double > getDiffusionArea( unsigned int fid ) const; + /// Virtual function to return scale factor for diffusion. 1 here. + vector< double > getDiffusionScaling( unsigned int fid ) const; + /// Volume of mesh Entry including abutting diff-coupled voxels + double extendedMeshEntryVolume( unsigned int fid ) const; + + ////////////////////////////////////////////////////////////////// + + /** + * Inherited virtual func. Returns number of MeshEntry in array + */ + unsigned int innerGetNumEntries() const; + /// Inherited virtual func. + void innerSetNumEntries( unsigned int n ); + + /// Inherited virtual, do nothing for now. + vector< unsigned int > getParentVoxel() const; + const vector< double >& vGetVoxelVolume() const; + const vector< double >& vGetVoxelMidpoint() const; + const vector< double >& getVoxelArea() const; + const vector< double >& getVoxelLength() const; + + /// Inherited virtual. Returns entire volume of compartment. + double vGetEntireVolume() const; + + /// Inherited virtual. Resizes len and dia of each voxel. + bool vSetVolumeNotRates( double volume ); + ////////////////////////////////////////////////////////////////// + // Dest funcs + ////////////////////////////////////////////////////////////////// + + /// Virtual func to make a mesh with specified Volume and numEntries + void innerBuildDefaultMesh( const Eref& e, + double volume, unsigned int numEntries ); + + void innerHandleRequestMeshStats( + const Eref& e, + const SrcFinfo2< unsigned int, vector< double > >* + meshStatsFinfo + ); + + void innerHandleNodeInfo( + const Eref& e, + unsigned int numNodes, unsigned int numThreads ); + + ////////////////////////////////////////////////////////////////// + // inherited virtual funcs for Boundary + ////////////////////////////////////////////////////////////////// + + void matchMeshEntries( const ChemCompt* other, + vector< VoxelJunction > & ret ) const; + + double nearest( double x, double y, double z, + unsigned int& index ) const; + + void indexToSpace( unsigned int index, + double& x, double& y, double& z ) const; + + ////////////////////////////////////////////////////////////////// + + static const Cinfo* initCinfo(); + + private: + /** + * Surrounding mesh. Use this for calculating + * all volume and diffusion terms, don't maintain any local + * variables. + */ + ObjId surround_; + const MeshCompt* parent_; + + Id insideMeshes_; /// EndoMeshes inside. Used to update. + + /** + * The reason why the powers don't have to be 1/3, 1/2 and 1 is + * because some organelles are not a simple linear vol relationship + * with their surround. I want to keep flexibility. + */ + double rPower_; /// rEndo = rScale * pow( surroundVol, rPower_) + double rScale_; /// rEndo = rScale * pow( surroundVol, rPower_) + double aPower_; /// aEndo = aScale * pow( surroundVol, aPower_) + double aScale_; /// aEndo = aScale * pow( surroundVol, aPower_) + double vPower_; /// vEndo = vScale * pow( surroundVol, vPower_) + double vScale_; /// vEndo = vScale * pow( surroundVol, vPower_) + + bool doAxialDiffusion_; /// Flag for axial diffusion +}; + +#endif // _ENDO_MESH_H diff --git a/mesh/MeshEntry.h b/mesh/MeshEntry.h index 9737ead2d72e9a5b6b9c5a382bd68ae232d88ad9..85521f17d6e03f99cc92659acdfd3afb8a1d6e12 100644 --- a/mesh/MeshEntry.h +++ b/mesh/MeshEntry.h @@ -19,7 +19,7 @@ enum MeshType { CUBOID, CYL, CYL_SHELL, CYL_SHELL_SEG, SPHERE, SPHERE_SHELL, SPHERE_SHELL_SEG, - TETRAHEDRON, DISK + TETRAHEDRON, DISK, ENDO }; class ChemCompt; @@ -58,6 +58,7 @@ class MeshEntry * 6: spherical shell * 7: spherical shell segment * 8: Tetrahedral + * 9: EndoMesh */ unsigned int getMeshType( const Eref& e ) const; diff --git a/mesh/NeuroMesh.cpp b/mesh/NeuroMesh.cpp index 2cf28411d06d8df1ed5d2b0edf922279e0eb2245..cb0ccc56a4ef31529ccafee18ddbcae3cc541c53 100644 --- a/mesh/NeuroMesh.cpp +++ b/mesh/NeuroMesh.cpp @@ -23,6 +23,7 @@ #include "NeuroMesh.h" #include "SpineEntry.h" #include "SpineMesh.h" +#include "EndoMesh.h" #include "../utility/numutil.h" #include "../utility/strutil.h" #include "../shell/Wildcard.h" @@ -1256,6 +1257,13 @@ void NeuroMesh::matchMeshEntries( const ChemCompt* other, matchCubeMeshEntries( other, ret ); return; } + const EndoMesh* em = dynamic_cast< const EndoMesh* >( other ); + if ( em ) + { + em->matchMeshEntries( this, ret ); + flipRet( ret ); + return; + } const SpineMesh* sm = dynamic_cast< const SpineMesh* >( other ); if ( sm ) { diff --git a/python/moose/moose_test.py b/python/moose/moose_test.py index 9cda2b80d81b4ffc20ff77bf0dae656c47763e7c..eeab9f9467b434bc71a2b6fdc2fe23f1e48cf342 100644 --- a/python/moose/moose_test.py +++ b/python/moose/moose_test.py @@ -87,6 +87,9 @@ class Command(object): self.process.terminate() thread.join() + if self.process.stderr is not None: + _logger.warn( '%s' % self.process.stderr.read() ) + return self.process.returncode def init_test_dir( ): @@ -116,7 +119,7 @@ def run_test( index, testfile, timeout, **kwargs): """ global test_status_ global total_ - pyExec = os.environ.get( 'PYTHON_EXECUTABLE', '/usr/bin/python' ) + pyExec = os.environ.get( 'PYTHON_EXECUTABLE', sys.executable ) cmd = Command( [ pyExec, testfile ] ) ti = time.time( ) diff --git a/python/rdesigneur/fixXreacs.py b/python/rdesigneur/fixXreacs.py new file mode 100644 index 0000000000000000000000000000000000000000..19379f5f189b60a02a4dcc3e17899509e5ce3e9c --- /dev/null +++ b/python/rdesigneur/fixXreacs.py @@ -0,0 +1,193 @@ +#################################################################### +# 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/python/rdesigneur/rdesigneur.py b/python/rdesigneur/rdesigneur.py index 30906a2502bbe8143afb96d91bd8cc9ff3211af5..0ef8d5c45c81c01f2a230e9f6cdcce9ffc50cd9d 100644 --- a/python/rdesigneur/rdesigneur.py +++ b/python/rdesigneur/rdesigneur.py @@ -28,6 +28,7 @@ import sys import time import rdesigneur.rmoogli as rmoogli +import rdesigneur.fixXreacs as fixXreacs from rdesigneur.rdesigneurProtos import * from moose.neuroml.NeuroML import NeuroML @@ -196,6 +197,7 @@ class rdesigneur: self._buildPlots() self._buildMoogli() self._buildStims() + self._configureHSolve() self._configureClocks() if self.verbose: self._printModelStats() @@ -896,7 +898,7 @@ class rdesigneur: 'inject':('CompartmentBase', 'setInject'), 'Ca':('CaConcBase', 'getCa'), 'n':('PoolBase', 'setN'), - 'conc':('PoolBase''setConc') + 'conc':('PoolBase', 'setConc') } stims = moose.Neutral( self.modelPath + '/stims' ) k = 0 @@ -918,7 +920,13 @@ class rdesigneur: moose.connect( func, 'valueOut', q, stimField ) ################################################################ - # Utility function for setting up clocks. + def _configureHSolve( self ): + if not self.turnOffElec: + hsolve = moose.HSolve( self.elecid.path + '/hsolve' ) + hsolve.dt = self.elecDt + hsolve.target = self.soma.path + +# Utility function for setting up clocks. def _configureClocks( self ): if self.turnOffElec: elecDt = 1e6 @@ -937,9 +945,6 @@ class rdesigneur: if not self.turnOffElec: # Assign the Function clock moose.setClock( 12, self.funcDt ) moose.setClock( 18, self.chemPlotDt ) - hsolve = moose.HSolve( self.elecid.path + '/hsolve' ) - hsolve.dt = elecDt - hsolve.target = self.soma.path sys.stdout.flush() ################################################################ ################################################################ @@ -1184,6 +1189,7 @@ class rdesigneur: return if not hasattr( self, 'dendCompt' ): raise BuildError( "configureSolvers: no chem meshes defined." ) + fixXreacs.fixXreacs( self.modelPath ) dmksolve = moose.Ksolve( self.dendCompt.path + '/ksolve' ) dmdsolve = moose.Dsolve( self.dendCompt.path + '/dsolve' ) dmstoich = moose.Stoich( self.dendCompt.path + '/stoich' ) @@ -1216,15 +1222,10 @@ class rdesigneur: pmstoich.dsolve = pmdsolve pmstoich.path = self.psdCompt.path + "/##" + # Here we should test what kind of geom we have to use # Put in cross-compartment diffusion between ksolvers dmdsolve.buildNeuroMeshJunctions( smdsolve, pmdsolve ) - # Put in cross-compartment reactions between ksolvers - smstoich.buildXreacs( pmstoich ) - #pmstoich.buildXreacs( smstoich ) - smstoich.buildXreacs( dmstoich ) - dmstoich.filterXreacs() - smstoich.filterXreacs() - pmstoich.filterXreacs() + # All the Xreac stuff in the solvers is now eliminated. # set up the connections so that the spine volume scaling can happen self.elecid.setSpineAndPsdMesh( self.spineCompt, self.psdCompt) diff --git a/python/rdesigneur/rdesigneurProtos.py b/python/rdesigneur/rdesigneurProtos.py index 43213b09be3209e52442b68c79d5f886c96c25df..1c22ea0cbc68a695f7c653cf0f4a0bba3e023231 100644 --- a/python/rdesigneur/rdesigneurProtos.py +++ b/python/rdesigneur/rdesigneurProtos.py @@ -325,6 +325,84 @@ def make_LCa( name = 'LCa', parent = '/library' ): ygate.tableA = yA ygate.tableB = yB return Ca +###################################################################### + +# Derived from : squid/electronics.py +# Description: +# Author: Subhasis Ray +# Maintainer: +# Created: Wed Feb 22 00:53:38 2012 (+0530) +# Version: +# Last-Updated: Fri May 04 16:35:40 2018 (+0530) +# By: Upi +# Update #: 221 + +# Change log: +# +# 2012-02-22 23:22:30 (+0530) Subha - the circuitry put in a class. +# 2018-05-04 23:22:30 (+0530) Upi - Adapted for Rdesigneur +# + +# Code: + +class ClampCircuit(moose.Neutral): + """Container for a Voltage-Clamp/Current clamp circuit.""" + defaults = { + 'level1': 25.0e-3, + 'width1': 50.0e-3, + 'delay1': 2.0e-3, + 'delay2': 1e3, + 'trigMode': 0, + 'delay3': 1e6 + } + def __init__(self, path ): + moose.Neutral.__init__(self, path) + ''' + self.pulsegen = moose.PulseGen(path+"/pulse") # holding voltage/current generator + self.pulsegen.count = 2 + self.pulsegen.baseLevel = -65.0e-3 + self.pulsegen.firstLevel = -40.0e-3 + self.pulsegen.firstWidth = 50.0e-3 + self.pulsegen.firstDelay = 2.0e-3 + self.pulsegen.secondDelay = 0.0 + self.pulsegen.trigMode = 2 + self.gate = moose.PulseGen(path+"/gate") # holding voltage/current generator + self.gate.level[0] = 1.0 + self.gate.delay[0] = 0.0 + self.gate.width[0] = 1e3 + moose.connect(self.gate, 'output', self.pulsegen, 'input') + ''' + self.lowpass = moose.RC(path+"/lowpass") # lowpass filter + self.lowpass.R = 1.0 + self.lowpass.C = 0.03 + self.vclamp = moose.DiffAmp(path+"/vclamp") + self.vclamp.gain = 1.0 + self.vclamp.saturation = 1e10 + self.iclamp = moose.DiffAmp(path+"/iclamp") + self.iclamp.gain = 0.0 + self.iclamp.saturation = 1e10 + self.pid = moose.PIDController(path+"/pid") + self.pid.gain = 0.5 + self.pid.tauI = 0.02e-3 + self.pid.tauD = 0.005e-3 + self.pid.saturation = 1e7 + # Connect voltage clamp circuitry + #moose.connect(self.pulsegen, "output", self.lowpass, "injectIn") + moose.connect(self.lowpass, "output", self.vclamp, "plusIn") + moose.connect(self.vclamp, "output", self.pid, "commandIn") + #moose.connect(compartment, "VmOut", self.pid, "sensedIn") + #moose.connect(self.pid, "output", compartment, "injectMsg") + addmsg1 = moose.Mstring( path + '/addmsg1' ) + addmsg1.value = './pid output .. injectMsg' + addmsg2 = moose.Mstring( path + '/addmsg2' ) + addmsg2.value = '.. VmOut ./pid sensedIn' + +def make_vclamp( name = 'Vclamp', parent = '/library' ): + if moose.exists( '/library/' + name ): + return + vclamp = ClampCircuit( parent + '/' + name ) + +###################################################################### ################################################################ # API function for building spine prototypes. Here we put in the diff --git a/python/setup.cmake.py b/python/setup.cmake.py index a3eaff221156e64e27cded558038f9fad2ac4e0a..d74a52426fa622a500bb935adeb8824c286b1734 100644 --- a/python/setup.cmake.py +++ b/python/setup.cmake.py @@ -21,6 +21,9 @@ import sys # guidelines. This caused havoc on our OBS build. from distutils.core import setup + +# Read version from VERSION created by cmake file. This file must be present for +# setup.cmake.py to work perfectly. script_dir = os.path.dirname( os.path.abspath( __file__ ) ) # Version file must be available. It MUST be written by cmake. Or create diff --git a/scheduling/Clock.cpp b/scheduling/Clock.cpp index bd1e6ca8b820e269d513958fcce55293002a502a..dc45989f3a781825554bb42f8958fc98a251f3c1 100644 --- a/scheduling/Clock.cpp +++ b/scheduling/Clock.cpp @@ -958,10 +958,12 @@ void Clock::buildDefaultTick() defaultTick_["ChemCompt"] = ~0U; defaultTick_["Cinfo"] = ~0U; defaultTick_["Clock"] = ~0U; + defaultTick_["ConcChan"] = ~0U; defaultTick_["CubeMesh"] = ~0U; defaultTick_["CylMesh"] = ~0U; defaultTick_["DiagonalMsg"] = ~0U; defaultTick_["Double"] = ~0U; + defaultTick_["EndoMesh"] = ~0U; defaultTick_["Finfo"] = ~0U; defaultTick_["Group"] = ~0U; defaultTick_["HHGate"] = ~0U;