/********************************************************************** ** 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 "ElementValueFinfo.h" #include "PoolBase.h" #include "ReacBase.h" #include "EnzBase.h" #include "CplxEnzBase.h" #include "FuncTerm.h" #include "RateTerm.h" #include "FuncRateTerm.h" #include "SparseMatrix.h" #include "KinSparseMatrix.h" #include "VoxelPoolsBase.h" #include "../mesh/VoxelJunction.h" #include "XferInfo.h" #include "ZombiePoolInterface.h" #include "../builtins/Variable.h" #include "../builtins/Function.h" #include "ZombieFunction.h" #include "Stoich.h" #include "lookupVolumeFromMesh.h" #include "../scheduling/Clock.h" #include "../shell/Shell.h" #include "../shell/Wildcard.h" const Cinfo* Stoich::initCinfo() { ////////////////////////////////////////////////////////////// // Field Definitions ////////////////////////////////////////////////////////////// static ElementValueFinfo< Stoich, string > path( "path", "Wildcard path for reaction system handled by Stoich", &Stoich::setPath, &Stoich::getPath ); static ValueFinfo< Stoich, Id > ksolve( "ksolve", "Id of Kinetic reaction solver class that works with this " "Stoich. " " Must be of class Ksolve, or Gsolve (at present) " " Must be assigned before the path is set.", &Stoich::setKsolve, &Stoich::getKsolve ); static ValueFinfo< Stoich, Id > dsolve( "dsolve", "Id of Diffusion solver class that works with this Stoich." " Must be of class Dsolve " " If left unset then the system will be assumed to work in a" " non-diffusive, well-stirred cell. If it is going to be " " used it must be assigned before the path is set.", &Stoich::setDsolve, &Stoich::getDsolve ); static ValueFinfo< Stoich, Id > compartment( "compartment", "Id of chemical compartment class that works with this Stoich." " Must be derived from class ChemCompt." " If left unset then the system will be assumed to work in a" " non-diffusive, well-stirred cell. If it is going to be " " used it must be assigned before the path is set.", &Stoich::setCompartment, &Stoich::getCompartment ); static ReadOnlyValueFinfo< Stoich, unsigned int > numVarPools( "numVarPools", "Number of time-varying pools to be computed by the " "numerical engine", &Stoich::getNumVarPools ); static ReadOnlyValueFinfo< Stoich, unsigned int > numBufPools( "numBufPools", "Number of buffered pools to be computed by the " "numerical engine. Includes pools controlled by functions.", &Stoich::getNumBufPools ); static ReadOnlyValueFinfo< Stoich, unsigned int > numAllPools( "numAllPools", "Total number of pools handled by the numerical engine. " "This includes variable ones, buffered ones, and functions. " "It includes local pools as well as cross-solver proxy pools.", &Stoich::getNumAllPools ); static ReadOnlyValueFinfo< Stoich, unsigned int > numProxyPools( "numProxyPools", "Number of pools here by proxy as substrates of a cross-" "compartment reaction.", &Stoich::getNumProxyPools ); static ReadOnlyValueFinfo< Stoich, vector< unsigned int > > poolIdMap( "poolIdMap", "Map to look up the index of the pool from its Id." "poolIndex = poolIdMap[ Id::value() - poolOffset ] " "where the poolOffset is the smallest Id::value. " "poolOffset is passed back as the last entry of this vector." " Any Ids that are not pools return EMPTY=~0. ", &Stoich::getPoolIdMap ); static ReadOnlyValueFinfo< Stoich, unsigned int > numRates( "numRates", "Total number of rate terms in the reaction system.", &Stoich::getNumRates ); // Stuff here for getting Stoichiometry matrix to manipulate in // Python. static ReadOnlyValueFinfo< Stoich, vector< int > > matrixEntry( "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", &Stoich::getMatrixEntry ); // Stuff here for getting Stoichiometry matrix to manipulate in // Python. static ReadOnlyValueFinfo< Stoich, vector< unsigned int > > columnIndex( "columnIndex", "Column Index of each matrix entry", &Stoich::getColIndex ); // Stuff here for getting Stoichiometry matrix to manipulate in // Python. static ReadOnlyValueFinfo< Stoich, vector< unsigned int > > rowStart( "rowStart", "Row start for each block of entries and column indices", &Stoich::getRowStart ); // Proxy pool information static ReadOnlyLookupValueFinfo< Stoich, Id, vector< Id > > proxyPools( "proxyPools", "Return vector of proxy pools for X-compt reactions between " "current stoich, and the argument, which is a StoichId. " "The returned pools belong to the compartment handling the " "Stoich specified in the argument. " "If no pools are found, return an empty vector.", &Stoich::getProxyPools ); static ReadOnlyValueFinfo< Stoich, int > status( "status", "Status of Stoich in the model building process. Values are: " "-1: Reaction path not yet assigned.\n " "0: Successfully built stoichiometry matrix.\n " "1: Warning: Missing a reactant in a Reac or Enz.\n " "2: Warning: Missing a substrate in an MMenz.\n " "3: Warning: Missing substrates as well as reactants.\n " "4: Warning: Compartment not defined.\n " "8: Warning: Neither Ksolve nor Dsolve defined.\n " "16: Warning: No objects found on path.\n " "", &Stoich::getStatus ); ////////////////////////////////////////////////////////////// // MsgDest Definitions ////////////////////////////////////////////////////////////// static DestFinfo unzombify( "unzombify", "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 " "voxel#, Used in adaptors changing spine vols.", new OpFunc2< Stoich, unsigned int, double >( &Stoich::scaleBufsAndRates ) ); ////////////////////////////////////////////////////////////// // SrcFinfo Definitions ////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// // SharedMsg Definitions ////////////////////////////////////////////////////////////// static Finfo* stoichFinfos[] = { &path, // ElementValue &ksolve, // Value &dsolve, // Value &compartment, // Value &numVarPools, // ReadOnlyValue &numBufPools, // ReadOnlyValue &numAllPools, // ReadOnlyValue &numProxyPools, // ReadOnlyValue &poolIdMap, // ReadOnlyValue &numRates, // ReadOnlyValue &matrixEntry, // ReadOnlyValue &columnIndex, // ReadOnlyValue &rowStart, // ReadOnlyValue &proxyPools, // ReadOnlyLookupValue &status, // ReadOnlyLookupValue &unzombify, // DestFinfo &buildXreacs, // DestFinfo &filterXreacs, // DestFinfo &scaleBufsAndRates, // DestFinfo }; static Dinfo< Stoich > dinfo; static Cinfo stoichCinfo ( "Stoich", Neutral::initCinfo(), stoichFinfos, sizeof( stoichFinfos ) / sizeof ( Finfo* ), &dinfo ); return &stoichCinfo; } ////////////////////////////////////////////////////////////// // Class definitions ////////////////////////////////////////////////////////////// static const Cinfo* stoichCinfo = Stoich::initCinfo(); ////////////////////////////////////////////////////////////// Stoich::Stoich() : useOneWay_( 0 ), path_( "" ), ksolve_(), // Must be reassigned to build stoich system. dsolve_(), // Must be assigned if diffusion is planned. compartment_(), // Must be assigned if diffusion is planned. kinterface_( 0 ), dinterface_( 0 ), rates_( 0 ), // No RateTerms yet. // uniqueVols_( 1, 1.0 ), numVoxels_( 1 ), status_( -1 ) { ; } Stoich::~Stoich() { unZombifyModel(); // Note that we cannot do the unZombify here, because it is too // prone to problems with the ordering of the delete operations // relative to the zombies. for ( vector< RateTerm* >::iterator j = rates_.begin(); j != rates_.end(); ++j ) delete *j; for ( vector< FuncTerm* >::iterator j = funcs_.begin(); j != funcs_.end(); ++j ) delete *j; /* * Do NOT delete FuncTerms, they are just pointers stolen from * the non-zombified objects. for ( vector< FuncTerm* >::iterator i = funcs_.begin(); i != funcs_.end(); ++i ) delete *i; */ } ////////////////////////////////////////////////////////////// // Field Definitions ////////////////////////////////////////////////////////////// void Stoich::setOneWay( bool v ) { useOneWay_ = v; } bool Stoich::getOneWay() const { return useOneWay_; } void Stoich::setPath( const Eref& e, string v ) { if ( path_ != "" && path_ != v ) { // unzombify( path_ ); cout << "Stoich::setPath: need to clear old path.\n"; status_ = -1; return; } if ( ksolve_ == Id() ) { cout << "Stoich::setPath: need to first set ksolve.\n"; status_ = -1; return; } vector< ObjId > elist; path_ = v; wildcardFind( path_, elist ); setElist( e, elist ); } void convWildcards( vector< Id >& ret, const vector< ObjId >& elist ) { ret.resize( elist.size() ); for ( unsigned int i = 0; i < elist.size(); ++i ) ret[i] = elist[i].id; } void filterWildcards( vector< Id >& ret, const vector< ObjId >& elist ) { ret.clear(); ret.reserve( elist.size() ); for ( vector< ObjId >::const_iterator i = elist.begin(); i != elist.end(); ++i ) { if ( i->element()->cinfo()->isA( "PoolBase" ) || i->element()->cinfo()->isA( "ReacBase" ) || i->element()->cinfo()->isA( "EnzBase" ) || i->element()->cinfo()->isA( "Function" ) ) ret.push_back( i->id ); } } void Stoich::setElist( const Eref& e, const vector< ObjId >& elist ) { if ( compartment_ == Id() ) { cout << "Warning: Stoich::setElist/setPath: Compartment not set. Aborting.\n"; status_ = 4; return; } if ( !( kinterface_ || dinterface_ ) ) { cout << "Warning: Stoich::setElist/setPath: Neither solver has been set. Aborting.\n"; status_ = 8; return; } status_ = 0; if ( kinterface_ ) kinterface_->setCompartment( compartment_ ); if ( dinterface_ ) dinterface_->setCompartment( compartment_ ); vector< Id > temp; filterWildcards( temp, elist ); if( temp.size() == 0 ) { cout << "Warning: Stoich::setElist/setPath: No kinetics objects found on path. Aborting.\n"; status_ = 16; return; } locateOffSolverReacs( compartment_, temp ); // allocateObjMap( temp ); allocateModel( temp ); if ( kinterface_ ) { // kinterface_->setNumPools( n ); kinterface_->setStoich( e.id() ); Shell* shell = reinterpret_cast< Shell* >( Id().eref().data() ); shell->doAddMsg( "Single", compartment_, "voxelVolOut", ksolve_, "voxelVol" ); } if ( dinterface_ ) { // dinterface_->setNumPools( n ); dinterface_->setStoich( e.id() ); } zombifyModel( e, temp ); 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 { return path_; } void Stoich::setKsolve( Id ksolve ) { ksolve_ = Id(); kinterface_ = 0; if ( ! ( ksolve.element()->cinfo()->isA( "Ksolve" ) || ksolve.element()->cinfo()->isA( "Gsolve" ) ) ) { cout << "Error: Stoich::setKsolve: invalid class assigned," " should be either Ksolve or Gsolve\n"; return; } ksolve_ = ksolve; kinterface_ = reinterpret_cast< ZombiePoolInterface* >( ksolve.eref().data() ); if ( ksolve.element()->cinfo()->isA( "Gsolve" ) ) setOneWay( true ); else setOneWay( false ); } Id Stoich::getKsolve() const { return ksolve_; } void Stoich::setDsolve( Id dsolve ) { dsolve_ = Id(); dinterface_ = 0; if ( ! ( dsolve.element()->cinfo()->isA( "Dsolve" ) ) ) { cout << "Error: Stoich::setDsolve: invalid class assigned," " should be Dsolve\n"; return; } dsolve_ = dsolve; dinterface_ = reinterpret_cast< ZombiePoolInterface* >( dsolve.eref().data() ); } Id Stoich::getDsolve() const { return dsolve_; } void Stoich::setCompartment( Id compartment ) { if ( ! ( compartment.element()->cinfo()->isA( "ChemCompt" ) ) ) { cout << "Error: Stoich::setCompartment: invalid class assigned," " should be ChemCompt or derived class\n"; return; } compartment_ = compartment; vector< double > temp; vector< double > vols = Field< vector < double > >::get( compartment, "voxelVolume" ); if ( vols.size() > 0 ) { numVoxels_ = vols.size(); sort( vols.begin(), vols.end() ); double bigVol = vols.back(); assert( bigVol > 0.0 ); temp.push_back( vols[0] / bigVol ); for ( vector< double >::iterator i = vols.begin(); i != vols.end(); ++i ) { if ( !doubleEq( temp.back(), *i / bigVol ) ) temp.push_back( *i / bigVol ); } } } Id Stoich::getCompartment() const { return compartment_; } unsigned int Stoich::getNumVarPools() const { return varPoolVec_.size(); } unsigned int Stoich::getNumBufPools() const { return bufPoolVec_.size(); } unsigned int Stoich::getNumAllPools() const { return varPoolVec_.size() + offSolverPoolVec_.size() + bufPoolVec_.size(); } unsigned int Stoich::getNumProxyPools() const { return offSolverPoolVec_.size(); } vector< unsigned int > Stoich::getPoolIdMap() const { if ( poolLookup_.size() == 0 ) return vector< unsigned int >( 1, 0 ); unsigned int minId = 1000000; unsigned int maxId = 0; unsigned int maxIndex = 0; map< Id, unsigned int >::const_iterator i; for ( i = poolLookup_.begin(); i != poolLookup_.end(); ++i ) { unsigned int j = i->first.value(); if ( j < minId ) minId = j; if ( j > maxId ) maxId = j; if ( maxIndex < i->second ) maxIndex = i->second; } vector< unsigned int > ret( maxId - minId + 2, ~0U ); for ( i = poolLookup_.begin(); i != poolLookup_.end(); ++i ) { unsigned int j = i->first.value() - minId; ret[j] = i->second; } ret[ ret.size() -1 ] = minId; return ret; } unsigned int Stoich::getNumRates() const { return rates_.size(); } /// Number of rate terms for reactions purely on this compartment. unsigned int Stoich::getNumCoreRates() const { return reacVec_.size() * (1 + useOneWay_) + enzVec_.size() * (2 + useOneWay_ ) + mmEnzVec_.size() + incrementFuncVec_.size(); } const RateTerm* Stoich::rates( unsigned int i ) const { assert( i < rates_.size() ); return rates_[i]; } const vector< RateTerm* >& Stoich::getRateTerms() const { return rates_; } unsigned int Stoich::getNumFuncs() const { return funcs_.size(); } const FuncTerm* Stoich::funcs( unsigned int i ) const { assert( i < funcs_.size() ); assert( funcs_[i]); return funcs_[i]; } vector< int > Stoich::getMatrixEntry() const { return N_.matrixEntry(); } vector< unsigned int > Stoich::getColIndex() const { return N_.colIndex(); } vector< unsigned int > Stoich::getRowStart() const { return N_.rowStart(); } vector< Id > Stoich::getProxyPools( Id i ) const { static vector< Id > dummy; if ( !i.element()->cinfo()->isA( "Stoich" ) ) { cout << "Warning: Stoich::getProxyPools: argument " << i << " is not a Stoich\n"; return dummy; } Id compt = Field< Id >::get( i, "compartment" ); map< Id, vector< Id > >::const_iterator j = offSolverPoolMap_.find( compt ); if ( j != offSolverPoolMap_.end() ) return j->second; return dummy; } int Stoich::getStatus() const { return status_; } ////////////////////////////////////////////////////////////// // Model setup functions ////////////////////////////////////////////////////////////// /** * Checks if specified reac is off solver. As side-effect it compiles * a vector of the pools that are off-solver, and the corresponding * compartments for those pools */ static bool isOffSolverReac( const Element* e, Id myCompt, // vector< Id >& offSolverPools, vector< Id >& poolCompts, map< Id, Id >& poolComptMap ) { assert( myCompt != Id() ); assert( myCompt.element()->cinfo()->isA( "ChemCompt" ) ); bool ret = false; vector< Id > neighbors; e->getNeighbors( neighbors, e->cinfo()->findFinfo( "subOut" )); vector< Id > n2; e->getNeighbors( n2, e->cinfo()->findFinfo( "prdOut" )); neighbors.insert( neighbors.end(), n2.begin(), n2.end() ); for ( vector< Id >::const_iterator j = neighbors.begin(); j != neighbors.end(); ++j ) { Id otherCompt = getCompt( *j ); if ( myCompt != otherCompt ) { // offSolverPools.push_back( *j ); poolCompts.push_back( otherCompt ); poolComptMap[ *j ] = otherCompt; // Avoids duplication of pools ret = true; if ( j->element()->cinfo()->isA( "BufPool" ) ) { cout << "Warning: Avoid BufPool: " << j->path() << "\n as reactant in cross-compartment reactions\n"; } } } return ret; } /** * Extracts and orders the compartments associated with a given reac. */ pair< Id, Id > extractCompts( const vector< Id >& compts ) { pair< Id, Id > ret; for ( vector< Id >::const_iterator i = compts.begin(); i != compts.end(); ++i ) { if ( ret.first == Id() ) { ret.first = *i; } else if ( ret.first != *i ) { if ( ret.second == Id() ) ret.second = *i; else { cout << "Error: Stoich::extractCompts: more than 2 compartments\n"; assert( 0 ); } } } if ( ( ret.second != Id() ) && ret.second < ret.first ) { Id temp = ret.first; ret.first = ret.second; ret.second = ret.first; } return ret; } void Stoich::locateOffSolverReacs( Id myCompt, vector< Id >& elist ) { offSolverPoolVec_.clear(); offSolverReacVec_.clear(); offSolverEnzVec_.clear(); offSolverMMenzVec_.clear(); offSolverReacCompts_.clear(); offSolverEnzCompts_.clear(); offSolverMMenzCompts_.clear(); map< Id, Id > poolComptMap; // < pool, compt > vector< Id > temp; temp.reserve( elist.size() ); for ( vector< Id >::const_iterator i = elist.begin(); i != elist.end(); ++i ) { const Element* e = i->element(); if ( e->cinfo()->isA( "ReacBase" ) || e->cinfo()->isA( "EnzBase" ) ) { vector< Id > compts; if ( isOffSolverReac( e, myCompt, compts, poolComptMap ) ) { if ( e->cinfo()->isA( "ReacBase" ) ) { offSolverReacVec_.push_back( *i ); offSolverReacCompts_.push_back( extractCompts(compts)); } else if ( e->cinfo()->isA( "CplxEnzBase" ) ) { offSolverEnzVec_.push_back( *i ); offSolverEnzCompts_.push_back( extractCompts(compts)); } else if ( e->cinfo()->isA( "EnzBase" ) ) { offSolverMMenzVec_.push_back( *i ); offSolverMMenzCompts_.push_back(extractCompts(compts)); } } else { temp.push_back( *i ); } } else { temp.push_back( *i ); } } offSolverPoolMap_.clear(); for ( map< Id, Id >::iterator i = poolComptMap.begin(); i != poolComptMap.end(); ++i ) { // fill in the map for activeOffSolverPools. offSolverPoolMap_[i->second].push_back( i->first ); } // Ensure we don't have repeats, and the pools are ordered by compt offSolverPoolVec_.clear(); for ( map< Id, vector< Id > >::iterator i = offSolverPoolMap_.begin(); i != offSolverPoolMap_.end(); ++i ) { if ( i->first != myCompt ) { offSolverPoolVec_.insert( offSolverPoolVec_.end(), i->second.begin(), i->second.end() ); } } elist = temp; } /////////////////////////////////////////////////////////////////// // Model allocation stuff here /////////////////////////////////////////////////////////////////// /* void Stoich::allocateObjMap( const vector< Id >& elist ) { vector< Id > temp( elist ); temp.insert( temp.end(), offSolverPoolVec_.begin(), offSolverPoolVec_.end() ); temp.insert( temp.end(), offSolverReacs_.begin(), offSolverReacs_.end() ); if ( temp.size() == 0 ) return; objMapStart_ = ~0; unsigned int maxId = 0; for ( vector< Id >::const_iterator i = temp.begin(); i != temp.end(); ++i ) { if ( objMapStart_ > i->value() ) objMapStart_ = i->value(); if ( maxId < i->value() ) maxId = i->value(); } objMap_.clear(); objMap_.resize( 1 + maxId - objMapStart_, 0 ); */ /** * If this assertion fails it usually means that the elist passed to * the solver is not properly restricted to objects located on the * current compartment. As a result of this, traversal for finding * off-compartment pools generates repeats with the ones in the elist. * Note that pool compartment assignment is determined by following * the mesh message, and thus a tree-based elist construction for * compartments may be incompatible with the generation of the lists * of off-compartment pools. It is up to the upstream code to * ensure that this is done properly. * * This assertion also fails if the Ids concerned had a dimension * greater than 1. */ /* assert( objMap_.size() >= temp.size() ); } */ /// Identifies and allocates objects in the Stoich. void Stoich::allocateModelObject( Id id ) { static const Cinfo* poolCinfo = Cinfo::find( "Pool" ); static const Cinfo* bufPoolCinfo = Cinfo::find( "BufPool" ); static const Cinfo* reacCinfo = Cinfo::find( "Reac" ); static const Cinfo* enzCinfo = Cinfo::find( "Enz" ); static const Cinfo* mmEnzCinfo = Cinfo::find( "MMenz" ); static const Cinfo* functionCinfo = Cinfo::find( "Function" ); static const Finfo* f1 = functionCinfo->findFinfo( "valueOut" ); static const SrcFinfo* sf = dynamic_cast< const SrcFinfo* >( f1 ); assert( sf ); Element* ei = id.element(); if ( ei->cinfo() == poolCinfo ) { // objMap_[ id.value() - objMapStart_ ] = numVarPools_; varPoolVec_.push_back( id ); // ++numVarPools_; } else if ( ei->cinfo() == bufPoolCinfo ) { bufPoolVec_.push_back( id ); } else if ( ei->cinfo() == mmEnzCinfo ) { mmEnzVec_.push_back( ei->id() ); // objMap_[ id.value() - objMapStart_ ] = numReac_; // ++numReac_; } else if ( ei->cinfo() == reacCinfo ) { reacVec_.push_back( ei->id() ); /* if ( useOneWay_ ) { objMap_[ id.value() - objMapStart_ ] = numReac_; numReac_ += 2; } else { objMap_[ id.value() - objMapStart_ ] = numReac_; ++numReac_; } */ } else if ( ei->cinfo() == enzCinfo ) { enzVec_.push_back( ei->id() ); /* if ( useOneWay_ ) { objMap_[ id.value() - objMapStart_ ] = numReac_; numReac_ += 3; } else { objMap_[ id.value() - objMapStart_ ] = numReac_; numReac_ += 2; } */ } else if ( ei->cinfo() == functionCinfo ) { vector< ObjId > tgt; vector< string > func; ei->getMsgTargetAndFunctions( 0, sf, tgt, func ); if ( func.size() > 0 && func[0] == "increment" ) { incrementFuncVec_.push_back( ei->id() ); // objMap_[ id.value() - objMapStart_ ] = numReac_; // numReac_++; } else if ( func.size() > 0 && func[0] == "setNumKf" ) { reacFuncVec_.push_back( ei->id() ); } else // Assume it controls the N of a pool. { poolFuncVec_.push_back( ei->id() ); // objMap_[ id.value() - objMapStart_ ] = numFunctions_; // ++numFunctions_; } } } // Sorts, unique, erase any extras. void myUnique( vector< Id >& v ) { sort( v.begin(), v.end() ); vector< Id >::iterator last = unique( v.begin(), v.end() ); v.erase( last, v.end() ); } /// Using the computed array sizes, now allocate space for them. void Stoich::resizeArrays() { myUnique( varPoolVec_ ); myUnique( bufPoolVec_ ); myUnique( offSolverPoolVec_ ); myUnique( reacVec_ ); myUnique( offSolverReacVec_ ); myUnique( enzVec_ ); myUnique( offSolverEnzVec_ ); myUnique( mmEnzVec_ ); myUnique( offSolverMMenzVec_ ); unsigned int totNumPools = varPoolVec_.size() + bufPoolVec_.size() + + offSolverPoolVec_.size(); species_.resize( totNumPools, 0 ); unsigned int totNumRates = ( reacVec_.size() + offSolverReacVec_.size() ) * (1+useOneWay_) + ( enzVec_.size() + offSolverEnzVec_.size() ) * (2 + useOneWay_ ) + mmEnzVec_.size() + offSolverMMenzVec_.size() + incrementFuncVec_.size(); rates_.resize( totNumRates, 0 ); funcs_.resize( poolFuncVec_.size(), 0 ); N_.setSize( totNumPools, totNumRates ); if ( kinterface_ ) kinterface_->setNumPools( totNumPools ); if ( dinterface_ ) // Only set up var pools managed locally. dinterface_->setNumPools( varPoolVec_.size() ); } /// Calculate sizes of all arrays, and allocate them. void Stoich::allocateModel( const vector< Id >& elist ) { // numVarPools_ = 0; // numReac_ = 0; // numFunctions_ = 0; // vector< Id > bufPools; varPoolVec_.clear(); bufPoolVec_.clear(); // offSolverPoolVec is filled up by the locateOffSolverReacs function reacVec_.clear(); enzVec_.clear(); mmEnzVec_.clear(); poolFuncVec_.clear(); incrementFuncVec_.clear(); reacFuncVec_.clear(); for ( vector< Id >::const_iterator i = elist.begin(); i != elist.end(); ++i ) allocateModelObject( *i ); resizeArrays(); buildPoolLookup(); buildRateTermLookup(); buildFuncLookup(); } /////////////////////////////////////////////////////////////////// // Build the lookup maps for conversion of Ids to internal indices. /////////////////////////////////////////////////////////////////// void Stoich::buildPoolLookup() { // The order of pools is: varPools, offSolverVarPools, bufPools. poolLookup_.clear(); int poolNum = 0; vector< Id >::iterator i; for ( i = varPoolVec_.begin(); i != varPoolVec_.end(); ++i ) poolLookup_[ *i ] = poolNum++; for ( i = offSolverPoolVec_.begin(); i != offSolverPoolVec_.end(); ++i) poolLookup_[ *i ] = poolNum++; for ( i = bufPoolVec_.begin(); i != bufPoolVec_.end(); ++i ) poolLookup_[ *i ] = poolNum++; } void Stoich::buildRateTermLookup() { // The order of pools is: varPools, offSolverVarPools, bufPools. rateTermLookup_.clear(); int termNum = 0; vector< Id >::iterator i; for ( i = reacVec_.begin(); i != reacVec_.end(); ++i ) { rateTermLookup_[ *i ] = termNum; termNum += 1 + useOneWay_; } for ( i = enzVec_.begin(); i != enzVec_.end(); ++i ) { rateTermLookup_[ *i ] = termNum; termNum += 2 + useOneWay_; } for ( i = mmEnzVec_.begin(); i != mmEnzVec_.end(); ++i ) { rateTermLookup_[ *i ] = termNum; termNum += 1; } for ( i=incrementFuncVec_.begin(); i != incrementFuncVec_.end(); ++i ) { rateTermLookup_[ *i ] = termNum; termNum += 1; } for ( i = offSolverReacVec_.begin(); i != offSolverReacVec_.end(); ++i) { rateTermLookup_[ *i ] = termNum; termNum += 1 + useOneWay_; } for ( i = offSolverEnzVec_.begin(); i != offSolverEnzVec_.end(); ++i ) { rateTermLookup_[ *i ] = termNum; termNum += 2 + useOneWay_; } for ( i=offSolverMMenzVec_.begin(); i != offSolverMMenzVec_.end(); ++i) { rateTermLookup_[ *i ] = termNum; termNum += 1; } } void Stoich::buildFuncLookup() { funcLookup_.clear(); int funcNum = 0; vector< Id >::iterator i; for ( i = poolFuncVec_.begin(); i != poolFuncVec_.end(); ++i ) funcLookup_[ *i ] = funcNum++; } /////////////////////////////////////////////////////////////////// // Stoich building stuff here for installing model components. /////////////////////////////////////////////////////////////////// void Stoich::installAndUnschedFunc( Id func, Id pool, double volScale ) { static const Cinfo* varCinfo = Cinfo::find( "Variable" ); static const Finfo* funcInputFinfo = varCinfo->findFinfo( "input" ); static const DestFinfo* df = dynamic_cast< const DestFinfo* >( funcInputFinfo ); assert( df ); // Unsched Func func.element()->setTick( -2 ); // Disable with option to resurrect. // Install the FuncTerm FuncTerm* ft = new FuncTerm(); Id ei( func.value() + 1 ); unsigned int numSrc = Field< unsigned int >::get( func, "numVars" ); vector< pair< Id, unsigned int> > srcPools; #ifndef NDEBUG unsigned int n = #endif ei.element()->getInputsWithTgtIndex( srcPools, df ); assert( numSrc == n ); vector< unsigned int > poolIndex( numSrc, 0 ); for ( unsigned int i = 0; i < numSrc; ++i ) { unsigned int j = srcPools[i].second; if ( j >= numSrc ) { cout << "Warning: Stoich::installAndUnschedFunc: tgt index not allocated, " << j << ", " << numSrc << endl; continue; } poolIndex[j] = convertIdToPoolIndex( srcPools[i].first ); } ft->setReactantIndex( poolIndex ); string expr = Field< string >::get( func, "expr" ); ft->setExpr( expr ); // Tie the output of the FuncTerm to the pool it controls. ft->setTarget( convertIdToPoolIndex( pool ) ); ft->setVolScale( volScale ); unsigned int funcIndex = convertIdToFuncIndex( func ); assert( funcIndex != ~0U ); funcs_[ funcIndex ] = ft; } void Stoich::installAndUnschedFuncRate( Id func, Id pool ) { static const Cinfo* varCinfo = Cinfo::find( "Variable" ); static const Finfo* funcInputFinfo = varCinfo->findFinfo( "input" ); static const DestFinfo* df = dynamic_cast< const DestFinfo* >( funcInputFinfo ); assert( df ); // Unsched Func func.element()->setTick( -2 ); // Disable with option to resurrect. // Note that we set aside this index during allocateModelObject unsigned int rateIndex = convertIdToReacIndex( func ); unsigned int tempIndex = convertIdToPoolIndex( pool ); assert( rateIndex != ~0U ); assert( tempIndex != ~0U ); // Install the FuncReac FuncRate* fr = new FuncRate( 1.0, tempIndex ); rates_[rateIndex] = fr; int stoichEntry = N_.get( tempIndex, rateIndex ); N_.set( tempIndex, rateIndex, stoichEntry + 1 ); Id ei( func.value() + 1 ); unsigned int numSrc = Field< unsigned int >::get( func, "numVars" ); vector< pair< Id, unsigned int > > srcPools; #ifndef NDEBUG unsigned int n = #endif ei.element()->getInputsWithTgtIndex( srcPools, df ); assert( numSrc == n ); vector< unsigned int > poolIndex( numSrc, 0 ); for ( unsigned int i = 0; i < numSrc; ++i ) { unsigned int j = srcPools[i].second; if ( j >= numSrc ) { cout << "Warning: Stoich::installAndUnschedFuncRate: tgt index not allocated, " << j << ", " << numSrc << endl; continue; } poolIndex[j] = convertIdToPoolIndex( srcPools[i].first ); } fr->setFuncArgIndex( poolIndex ); string expr = Field< string >::get( func, "expr" ); fr->setExpr( expr ); } void Stoich::installAndUnschedFuncReac( Id func, Id reac ) { static const Cinfo* varCinfo = Cinfo::find( "Variable" ); // static const Finfo* funcSrcFinfo = varCinfo->findFinfo( "input" ); static const Finfo* funcSrcFinfo = varCinfo->findFinfo( "input" ); assert( funcSrcFinfo ); // Unsched Func func.element()->setTick( -2 ); // Disable with option to resurrect. unsigned int rateIndex = convertIdToReacIndex( reac ); assert( rateIndex != ~0U ); // Install the FuncReac double k = rates_[rateIndex]->getR1(); vector< unsigned int > reactants; unsigned int numForward = rates_[rateIndex]->getReactants( reactants ); // The reactants vector has both substrates and products. reactants.resize( numForward ); FuncReac* fr = new FuncReac( k, reactants ); delete rates_[rateIndex]; rates_[rateIndex] = fr; Id ei( func.value() + 1 ); unsigned int numSrc = Field< unsigned int >::get( func, "numVars" ); vector< Id > srcPools; #ifndef NDEBUG unsigned int n = #endif ei.element()->getNeighbors( srcPools, funcSrcFinfo); assert( numSrc == n ); vector< unsigned int > poolIndex( numSrc, 0 ); for ( unsigned int i = 0; i < numSrc; ++i ) poolIndex[i] = convertIdToPoolIndex( srcPools[i] ); fr->setFuncArgIndex( poolIndex ); string expr = Field< string >::get( func, "expr" ); fr->setExpr( expr ); } void Stoich::convertRatesToStochasticForm() { for ( unsigned int i = 0; i < rates_.size(); ++i ) { vector< unsigned int > molIndex; if ( rates_[i]->getReactants( molIndex ) > 1 ) { if ( molIndex.size() == 2 && molIndex[0] == molIndex[1] ) { RateTerm* oldRate = rates_[i]; rates_[ i ] = new StochSecondOrderSingleSubstrate( oldRate->getR1(), molIndex[ 0 ] ); delete oldRate; } else if ( molIndex.size() > 2 ) { RateTerm* oldRate = rates_[ i ]; rates_[i] = new StochNOrder( oldRate->getR1(), molIndex ); delete oldRate; } } } } 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 ////////////////////////////////////////////////////////////// /// Returns Function, if any, acting as src of specified msg into pa. static Id findFuncMsgSrc( Id pa, const string& msg ) { const Finfo *finfo = pa.element()->cinfo()->findFinfo( msg ); if ( !finfo ) return Id(); vector< Id > ret; if ( pa.element()->getNeighbors( ret, finfo ) > 0 ) { if ( ret[0].element()->cinfo()->isA( "Function" ) ) 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 ) { static const Cinfo* zfCinfo = Cinfo::find( "ZombieFunction" ); Id funcId = findFuncMsgSrc( pool, "setN" ); if ( funcId != Id() ) { Element* fe = funcId.element(); installAndUnschedFunc( funcId, pool, 1.0 ); ZombieFunction::zombify( fe, zfCinfo,ksolve_,dsolve_); } else { funcId = findFuncMsgSrc( pool, "setConc" ); if ( funcId != Id() ) { // cout << "Warning: Stoich::zombifyModel: Prefer to use setN rather than setConc:" << pool.path() << endl; Element* fe = funcId.element(); double vol = Field< double >::get( pool, "volume" ); installAndUnschedFunc( funcId, pool, vol * NA ); ZombieFunction::zombify( fe, zfCinfo,ksolve_,dsolve_); } } return funcId; } // e is the stoich Eref, elist is list of all Ids to zombify. void Stoich::zombifyModel( const Eref& e, const vector< Id >& elist ) { static const Cinfo* poolCinfo = Cinfo::find( "Pool" ); static const Cinfo* bufPoolCinfo = Cinfo::find( "BufPool" ); static const Cinfo* reacCinfo = Cinfo::find( "Reac" ); static const Cinfo* enzCinfo = Cinfo::find( "Enz" ); static const Cinfo* mmEnzCinfo = Cinfo::find( "MMenz" ); static const Cinfo* zombiePoolCinfo = Cinfo::find( "ZombiePool" ); static const Cinfo* zombieBufPoolCinfo = Cinfo::find( "ZombieBufPool"); static const Cinfo* zombieReacCinfo = Cinfo::find( "ZombieReac"); static const Cinfo* zombieMMenzCinfo = Cinfo::find( "ZombieMMenz"); static const Cinfo* zombieEnzCinfo = Cinfo::find( "ZombieEnz"); static const Cinfo* zfCinfo = Cinfo::find( "ZombieFunction" ); // static const Finfo* funcSrcFinfo = Cinfo::find( "Function")->findFinfo( "valueOut" ); // vector< Id > meshEntries; vector< Id > temp = elist; temp.insert( temp.end(), offSolverReacVec_.begin(), offSolverReacVec_.end() ); temp.insert( temp.end(), offSolverEnzVec_.begin(), offSolverEnzVec_.end() ); temp.insert( temp.end(), offSolverMMenzVec_.begin(), offSolverMMenzVec_.end() ); for ( vector< Id >::const_iterator i = temp.begin(); i != temp.end(); ++i ) { Element* ei = i->element(); if ( ei->cinfo() == poolCinfo ) { // We need to check the increment message before we zombify the // pool, because ZombiePool doesn't have this message. Id funcId = findFuncMsgSrc( *i, "increment" ); double concInit = Field< double >::get( ObjId( ei->id(), 0 ), "concInit" ); // Look for func setting rate of change of pool // Id funcId = Neutral::child( i->eref(), "func" ); if ( funcId != Id() ) { // cout << "Found Msg src for increment at " << funcId.path() << endl; Element* fe = funcId.element(); installAndUnschedFuncRate( funcId, (*i) ); ZombieFunction::zombify( fe, zfCinfo,ksolve_,dsolve_); } else { funcId = zombifyPoolFuncWithScaling( *i ); } PoolBase::zombify( ei, zombiePoolCinfo, ksolve_, dsolve_ ); ei->resize( numVoxels_ ); for ( unsigned int j = 0; j < numVoxels_; ++j ) { ObjId oi( ei->id(), j ); Field< double >::set( oi, "concInit", concInit ); } } else if ( ei->cinfo() == bufPoolCinfo ) { double concInit = Field< double >::get( ObjId( ei->id(), 0 ), "concInit" ); // Look for func setting conc of pool // Id funcId = Neutral::child( i->eref(), "func" ); Id funcId = zombifyPoolFuncWithScaling( *i ); if ( funcId == Id() ) { funcId = findFuncMsgSrc( *i, "increment" ); if ( funcId != Id() ) { cout << "Warning: Stoich::zombifyModel: Probably you don't want to send increment to a BufPool:" << i->path() << endl; Element* fe = funcId.element(); installAndUnschedFuncRate( funcId, (*i) ); ZombieFunction::zombify( fe, zfCinfo,ksolve_,dsolve_); } } PoolBase::zombify( ei, zombieBufPoolCinfo, ksolve_, dsolve_ ); ei->resize( numVoxels_ ); for ( unsigned int j = 0; j < numVoxels_; ++j ) { ObjId oi( ei->id(), j ); Field< double >::set( oi, "concInit", concInit ); } } else if ( ei->cinfo() == reacCinfo ) { ReacBase::zombify( ei, zombieReacCinfo, e.id() ); // Id funcId = Neutral::child( i->eref(), "func" ); Id funcId = findFuncMsgSrc( *i, "setNumKf" ); if ( funcId != Id() ) { Element* fe = funcId.element(); installAndUnschedFuncReac( funcId, (*i) ); ZombieFunction::zombify( fe, zfCinfo,ksolve_,dsolve_); /* } else { cout << "Warning: Stoich::zombifyModel: Failed to connect Func to Reac :" << i->path() << endl; return; */ } } else if ( ei->cinfo() == mmEnzCinfo ) { EnzBase::zombify( ei, zombieMMenzCinfo, e.id() ); } else if ( ei->cinfo() == enzCinfo ) { CplxEnzBase::zombify( ei, zombieEnzCinfo, e.id() ); } } } void Stoich::unZombifyPools() { static const Cinfo* poolCinfo = Cinfo::find( "Pool" ); static const Cinfo* bufPoolCinfo = Cinfo::find( "BufPool" ); static const Cinfo* zombiePoolCinfo = Cinfo::find( "ZombiePool" ); static const Cinfo* zombieBufPoolCinfo = Cinfo::find( "ZombieBufPool"); unsigned int i; for ( i = 0; i < varPoolVec_.size(); ++i ) { Element* e = varPoolVec_[i].element(); if ( !e || e->isDoomed() ) continue; if ( e != 0 && e->cinfo() == zombiePoolCinfo ) PoolBase::zombify( e, poolCinfo, Id(), Id() ); } for ( i = 0; i < bufPoolVec_.size(); ++i ) { Element* e = bufPoolVec_[i].element(); if ( !e || e->isDoomed() ) continue; if ( e != 0 && e->cinfo() == zombieBufPoolCinfo ) PoolBase::zombify( e, bufPoolCinfo, Id(), Id() ); } } void Stoich::unZombifyModel() { static const Cinfo* reacCinfo = Cinfo::find( "Reac" ); static const Cinfo* enzCinfo = Cinfo::find( "Enz" ); static const Cinfo* mmEnzCinfo = Cinfo::find( "MMenz" ); static const Cinfo* functionCinfo = Cinfo::find( "Function"); static const Cinfo* zombieReacCinfo = Cinfo::find( "ZombieReac"); static const Cinfo* zombieMMenzCinfo = Cinfo::find( "ZombieMMenz"); static const Cinfo* zombieEnzCinfo = Cinfo::find( "ZombieEnz"); static const Cinfo* zombieFunctionCinfo = Cinfo::find( "ZombieFunction"); unZombifyPools(); vector< Id > temp = reacVec_; temp.insert( temp.end(), offSolverReacVec_.begin(), offSolverReacVec_.end() ); for ( vector< Id >::iterator i = temp.begin(); i != temp.end(); ++i ) { Element* e = i->element(); if ( e != 0 && e->cinfo() == zombieReacCinfo ) ReacBase::zombify( e, reacCinfo, Id() ); } temp = mmEnzVec_; temp.insert( temp.end(), offSolverMMenzVec_.begin(), offSolverMMenzVec_.end() ); for ( vector< Id >::iterator i = temp.begin(); i != temp.end(); ++i ) { Element* e = i->element(); if ( e != 0 && e->cinfo() == zombieMMenzCinfo ) EnzBase::zombify( e, mmEnzCinfo, Id() ); } temp = enzVec_; temp.insert( temp.end(), offSolverEnzVec_.begin(), offSolverEnzVec_.end() ); for ( vector< Id >::iterator i = temp.begin(); i != temp.end(); ++i ) { Element* e = i->element(); if ( e != 0 && e->cinfo() == zombieEnzCinfo ) CplxEnzBase::zombify( e, enzCinfo, Id() ); } temp = poolFuncVec_; temp.insert( temp.end(), incrementFuncVec_.begin(), incrementFuncVec_.end() ); for ( vector< Id >::iterator i = temp.begin(); i != temp.end(); ++i ) { Element* e = i->element(); if ( e != 0 && e->cinfo() == zombieFunctionCinfo ) { ZombieFunction::zombify( e, functionCinfo, Id(), Id() ); //cout << "ZombieFunction unzombify: " << e->getTick() << endl; } if ( e != 0 && e->getTick() == -2 ) { int t = Clock::lookupDefaultTick( e->cinfo()->name() ); e->setTick( t ); } } } unsigned int Stoich::convertIdToPoolIndex( Id id ) const { map< Id, unsigned int >::const_iterator i = poolLookup_.find( id ); if ( i != poolLookup_.end() ) { return i->second; } return ~0U; } unsigned int Stoich::convertIdToReacIndex( Id id ) const { map< Id, unsigned int >::const_iterator i = rateTermLookup_.find( id ); if ( i != rateTermLookup_.end() ) { return i->second; } return ~0U; } unsigned int Stoich::convertIdToFuncIndex( Id id ) const { map< Id, unsigned int >::const_iterator i = funcLookup_.find( id ); if ( i != funcLookup_.end() ) { return i->second; } return ~0U; } ZeroOrder* Stoich::makeHalfReaction( double rate, const vector< Id >& reactants ) { ZeroOrder* rateTerm = 0; if ( reactants.size() == 1 ) { rateTerm = new FirstOrder( rate, convertIdToPoolIndex( reactants[0] ) ); } else if ( reactants.size() == 2 ) { rateTerm = new SecondOrder( rate, convertIdToPoolIndex( reactants[0] ), convertIdToPoolIndex( reactants[1] ) ); } else if ( reactants.size() > 2 ) { vector< unsigned int > temp; for ( unsigned int i = 0; i < reactants.size(); ++i ) temp.push_back( convertIdToPoolIndex( reactants[i] ) ); rateTerm = new NOrder( rate, temp ); } else { cout << "Warning: Stoich::makeHalfReaction: no reactants\n"; status_ |= 1; rateTerm = new ZeroOrder(0.0); // Dummy RateTerm to avoid crash. } return rateTerm; } void Stoich::installReaction( Id reacId, const vector< Id >& subs, const vector< Id >& prds ) { static vector< Id > dummy; unsigned int rateIndex = innerInstallReaction( reacId, subs, prds ); if ( rateIndex < getNumCoreRates() ) // Only handle off-compt reacs return; vector< Id > subCompt; vector< Id > prdCompt; for ( vector< Id >::const_iterator i = subs.begin(); i != subs.end(); ++i ) subCompt.push_back( getCompt( *i ).id ); for ( vector< Id >::const_iterator i = prds.begin(); i != prds.end(); ++i ) prdCompt.push_back( getCompt( *i ).id ); assert ( rateIndex - getNumCoreRates() == subComptVec_.size() ); assert ( rateIndex - getNumCoreRates() == prdComptVec_.size() ); if ( useOneWay_ ) { subComptVec_.push_back( subCompt ); subComptVec_.push_back( prdCompt ); prdComptVec_.push_back( dummy ); prdComptVec_.push_back( dummy ); } else { subComptVec_.push_back( subCompt ); prdComptVec_.push_back( prdCompt ); } } /** * This takes the specified forward and reverse half-reacs belonging * to the specified Reac, and builds them into the Stoich. */ unsigned int Stoich::innerInstallReaction( Id reacId, const vector< Id >& subs, const vector< Id >& prds ) { ZeroOrder* forward = makeHalfReaction( 0, subs ); ZeroOrder* reverse = makeHalfReaction( 0, prds ); unsigned int rateIndex = convertIdToReacIndex( reacId ); unsigned int revRateIndex = rateIndex; if ( useOneWay_ ) { rates_[ rateIndex ] = forward; revRateIndex = rateIndex + 1; rates_[ revRateIndex ] = reverse; } else { rates_[ rateIndex ] = new BidirectionalReaction( forward, reverse ); } vector< unsigned int > molIndex; vector< double > reacScaleSubstrates; vector< double > reacScaleProducts; if ( useOneWay_ ) { unsigned int numReactants = forward->getReactants( molIndex ); for ( unsigned int i = 0; i < numReactants; ++i ) { int temp = N_.get( molIndex[i], rateIndex ); N_.set( molIndex[i], rateIndex, temp - 1 ); temp = N_.get( molIndex[i], revRateIndex ); N_.set( molIndex[i], revRateIndex, temp + 1 ); } numReactants = reverse->getReactants( molIndex ); for ( unsigned int i = 0; i < numReactants; ++i ) { int temp = N_.get( molIndex[i], rateIndex ); N_.set( molIndex[i], rateIndex, temp + 1 ); temp = N_.get( molIndex[i], revRateIndex ); N_.set( molIndex[i], revRateIndex, temp - 1 ); } } else { unsigned int numReactants = forward->getReactants( molIndex ); for ( unsigned int i = 0; i < numReactants; ++i ) { int temp = N_.get( molIndex[i], rateIndex ); N_.set( molIndex[i], rateIndex, temp - 1 ); } numReactants = reverse->getReactants( molIndex ); for ( unsigned int i = 0; i < numReactants; ++i ) { int temp = N_.get( molIndex[i], revRateIndex ); N_.set( molIndex[i], rateIndex, temp + 1 ); } } return rateIndex; } void installDummy( RateTerm** entry, Id enzId, const string& s ) { cout << "Warning: Stoich::installMMenz: No " << s << " for: " << enzId.path() << endl; *entry = new ZeroOrder( 0.0 ); } /** * This takes the baseclass for an MMEnzyme and builds the * MMenz into the Stoich. */ void Stoich::installMMenz( Id enzId, const vector< Id >& enzMols, const vector< Id >& subs, const vector< Id >& prds ) { MMEnzymeBase* meb; unsigned int enzSiteIndex = convertIdToReacIndex( enzId ); RateTerm** entry = &rates_[enzSiteIndex]; if ( enzMols.size() != 1 ) { installDummy( entry, enzId, "enzmols" ); status_ |= 2; return; } if ( prds.size() < 1 ) { installDummy( entry, enzId, "products" ); status_ |= 1; return; } unsigned int enzIndex = convertIdToPoolIndex( enzMols[0] ); if ( subs.size() == 1 ) { unsigned int subIndex = convertIdToPoolIndex( subs[0] ); meb = new MMEnzyme1( 1, 1, enzIndex, subIndex ); } else if ( subs.size() > 1 ) { vector< unsigned int > v; for ( unsigned int i = 0; i < subs.size(); ++i ) v.push_back( convertIdToPoolIndex( subs[i] ) ); ZeroOrder* rateTerm = new NOrder( 1.0, v ); meb = new MMEnzyme( 1, 1, enzIndex, rateTerm ); } else { installDummy( entry, enzId, "substrates" ); status_ |= 2; return; } installMMenz( meb, enzSiteIndex, subs, prds ); if ( enzSiteIndex < getNumCoreRates() ) // Only handle off-compt reacs return; vector< Id > subCompt; vector< Id > dummy; for ( vector< Id >::const_iterator i = subs.begin(); i != subs.end(); ++i ) subCompt.push_back( getCompt( *i ).id ); subComptVec_.push_back( subCompt ); prdComptVec_.push_back( dummy ); assert ( enzSiteIndex - getNumCoreRates() == subComptVec_.size() ); assert ( enzSiteIndex - getNumCoreRates() == prdComptVec_.size() ); } /// This is the internal variant to install the MMenz. void Stoich::installMMenz( MMEnzymeBase* meb, unsigned int rateIndex, const vector< Id >& subs, const vector< Id >& prds ) { rates_[rateIndex] = meb; for ( unsigned int i = 0; i < subs.size(); ++i ) { unsigned int poolIndex = convertIdToPoolIndex( subs[i] ); int temp = N_.get( poolIndex, rateIndex ); N_.set( poolIndex, rateIndex, temp - 1 ); } for ( unsigned int i = 0; i < prds.size(); ++i ) { unsigned int poolIndex = convertIdToPoolIndex( prds[i] ); int temp = N_.get( poolIndex, rateIndex ); N_.set( poolIndex, rateIndex, temp + 1 ); } } // Handles dangling enzymes. void Stoich::installDummyEnzyme( Id enzId, Id enzMolId ) { ZeroOrder* r1 = new ZeroOrder( 0.0 ); // Dummy ZeroOrder* r2 = new ZeroOrder( 0.0 ); // Dummy ZeroOrder* r3 = new ZeroOrder( 0.0 ); // Dummy vector< Id > dummy; unsigned int rateIndex = convertIdToReacIndex( enzId ); if ( useOneWay_ ) { rates_[ rateIndex ] = r1; rates_[ rateIndex + 1 ] = r2; rates_[ rateIndex + 2 ] = r3; } else { rates_[ rateIndex ] = new BidirectionalReaction( r1, r2 ); rates_[ rateIndex + 1 ] = r3; } status_ = 1; } void Stoich::installEnzyme( Id enzId, Id enzMolId, Id cplxId, const vector< Id >& subs, const vector< Id >& prds ) { vector< Id > temp( subs ); temp.insert( temp.begin(), enzMolId ); ZeroOrder* r1 = makeHalfReaction( 0, temp ); temp.clear(); temp.resize( 1, cplxId ); ZeroOrder* r2 = makeHalfReaction( 0, temp ); ZeroOrder* r3 = makeHalfReaction( 0, temp ); installEnzyme( r1, r2, r3, enzId, enzMolId, prds ); unsigned int rateIndex = convertIdToReacIndex( enzId ); if ( rateIndex < getNumCoreRates() ) // Only handle off-compt reacs return; vector< Id > subCompt; vector< Id > dummy; for ( vector< Id >::const_iterator i = subs.begin(); i != subs.end(); ++i ) subCompt.push_back( getCompt( *i ).id ); if ( useOneWay_ ) { // enz is split into 3 reactions. Only the first might be off-compt subComptVec_.push_back( subCompt ); subComptVec_.push_back( dummy ); subComptVec_.push_back( dummy ); prdComptVec_.push_back( dummy ); prdComptVec_.push_back( dummy ); prdComptVec_.push_back( dummy ); //assert ( 2 + rateIndex - getNumCoreRates() == subComptVec_.size()); //assert ( 2 + rateIndex - getNumCoreRates() == prdComptVec_.size()); } else { // enz is split into 2 reactions. Only the first might be off-compt subComptVec_.push_back( subCompt ); subComptVec_.push_back( dummy ); prdComptVec_.push_back( dummy ); prdComptVec_.push_back( dummy ); //assert ( 1+rateIndex - getNumCoreRates() == subComptVec_.size() ); // assert ( 1+rateIndex - getNumCoreRates() == prdComptVec_.size() ); } } void Stoich::installEnzyme( ZeroOrder* r1, ZeroOrder* r2, ZeroOrder* r3, Id enzId, Id enzMolId, const vector< Id >& prds ) { unsigned int rateIndex = convertIdToReacIndex( enzId ); if ( useOneWay_ ) { rates_[ rateIndex ] = r1; rates_[ rateIndex + 1 ] = r2; rates_[ rateIndex + 2 ] = r3; } else { rates_[ rateIndex ] = new BidirectionalReaction( r1, r2 ); rates_[ rateIndex + 1 ] = r3; } vector< unsigned int > poolIndex; unsigned int numReactants = r2->getReactants( poolIndex ); assert( numReactants == 1 ); // Should be cplx as the only product unsigned int cplxPool = poolIndex[0]; if ( useOneWay_ ) { numReactants = r1->getReactants( poolIndex ); // Substrates for ( unsigned int i = 0; i < numReactants; ++i ) { int temp = N_.get( poolIndex[i], rateIndex ); // terms for r1 N_.set( poolIndex[i], rateIndex, temp - 1 ); temp = N_.get( poolIndex[i], rateIndex + 1 ); //terms for r2 N_.set( poolIndex[i], rateIndex + 1, temp + 1 ); } int temp = N_.get( cplxPool, rateIndex ); // term for r1 N_.set( cplxPool, rateIndex, temp + 1 ); temp = N_.get( cplxPool, rateIndex + 1 ); // term for r2 N_.set( cplxPool, rateIndex + 1, temp -1 ); } else // Regular bidirectional reactions. { numReactants = r1->getReactants( poolIndex ); // Substrates for ( unsigned int i = 0; i < numReactants; ++i ) { int temp = N_.get( poolIndex[i], rateIndex ); N_.set( poolIndex[i], rateIndex, temp - 1 ); } int temp = N_.get( cplxPool, rateIndex ); N_.set( cplxPool, rateIndex, temp + 1 ); } // Now assign reaction 3. The complex is the only substrate here. // Reac 3 is already unidirectional, so all we need to do to handle // one-way reactions is to get the index right. unsigned int reac3index = ( useOneWay_ ) ? rateIndex + 2 : rateIndex + 1; int temp = N_.get( cplxPool, reac3index ); N_.set( cplxPool, reac3index, temp - 1 ); // For the products, we go to the prd list directly. for ( unsigned int i = 0; i < prds.size(); ++i ) { unsigned int j = convertIdToPoolIndex( prds[i] ); int temp = N_.get( j, reac3index ); N_.set( j, reac3index, temp + 1 ); } // Enz is also a product here. unsigned int enzPool = convertIdToPoolIndex( enzMolId ); temp = N_.get( enzPool, reac3index ); N_.set( enzPool, reac3index, temp + 1 ); } ////////////////////////////////////////////////////////////// // Field interface functions ////////////////////////////////////////////////////////////// /** * Sets the forward rate v (given in millimoloar concentration units) * for the specified reaction throughout the compartment in which the * reaction lives. Internally the stoich uses #/voxel units so this * involves querying the volume subsystem about volumes for each * voxel, and scaling accordingly. * For now assume a uniform voxel volume and hence just convert on * 0 meshIndex. */ void Stoich::setReacKf( const Eref& e, double v ) const { unsigned int i = convertIdToReacIndex( e.id() ); if ( i != ~0U ) { // rates_[ i ]->setR1( v / volScale ); rates_[ i ]->setR1( v ); kinterface_->updateRateTerms( i ); } } /** * For now assume a single rate term. */ void Stoich::setReacKb( const Eref& e, double v ) const { unsigned int i = convertIdToReacIndex( e.id() ); if ( i == ~0U ) return; if ( useOneWay_ ) { rates_[ i + 1 ]->setR1( v ); kinterface_->updateRateTerms( i + 1 ); } else { rates_[ i ]->setR2( v ); kinterface_->updateRateTerms( i ); } } // This uses Km to set the StoichR1, which is actually in # units. // It is OK to do for the Stoich because the volume is defined to be // 1.66e-21, such that conc == #. void Stoich::setMMenzKm( const Eref& e, double v ) const { // Identify MMenz rate term unsigned int index = convertIdToReacIndex( e.id() ); RateTerm* rt = rates_[ index ]; //MMEnzymeBase* enz = dynamic_cast< MMEnzymeBase* >( rt ); //assert( enz ); // Identify MMenz Enzyme substrate. I would have preferred the parent, // but that gets messy. // unsigned int enzMolIndex = enz->getEnzIndex(); // This function can be replicated to handle multiple different voxels. /* vector< double > vols; getReactantVols( e, subOut, vols ); if ( vols.size() == 0 ) { cerr << "Error: Stoich::setMMenzKm: no substrates for enzyme " << e << endl; return; } */ // Do scaling and assignment. rt->setR1( v ); kinterface_->updateRateTerms( index ); } double Stoich::getMMenzNumKm( const Eref& e ) const { return getR1( e ); } void Stoich::setMMenzKcat( const Eref& e, double v ) const { unsigned int index = convertIdToReacIndex( e.id() ); RateTerm* rt = rates_[ index ]; // MMEnzymeBase* enz = dynamic_cast< MMEnzymeBase* >( rt ); // assert( enz ); rt->setR2( v ); kinterface_->updateRateTerms( index ); } double Stoich::getMMenzKcat( const Eref& e ) const { return getR2( e ); } /// Later handle all the volumes when this conversion is done. void Stoich::setEnzK1( const Eref& e, double v ) const { unsigned int index = convertIdToReacIndex( e.id() ); rates_[ index ]->setR1( v ); kinterface_->updateRateTerms( index ); } void Stoich::setEnzK2( const Eref& e, double v ) const { unsigned int index = convertIdToReacIndex( e.id() ); if ( useOneWay_ ) { rates_[ index + 1 ]->setR1( v ); kinterface_->updateRateTerms( index + 1 ); } else { rates_[ index ]->setR2( v ); kinterface_->updateRateTerms( index ); } } void Stoich::setEnzK3( const Eref& e, double v ) const { unsigned int index = convertIdToReacIndex( e.id() ); if ( useOneWay_ ) { rates_[ index + 2 ]->setR1( v ); kinterface_->updateRateTerms( index + 2 ); } else { rates_[ index + 1 ]->setR1( v ); kinterface_->updateRateTerms( index + 1 ); } } double Stoich::getEnzNumK1( const Eref& e ) const { return getR1( e ); } double Stoich::getEnzK2( const Eref& e ) const { if ( useOneWay_ ) return getR1offset1( e ); else return getR2( e ); } double Stoich::getEnzK3( const Eref& e ) const { if ( useOneWay_ ) return getR1offset2( e ); else return getR1offset1( e ); } /** * Looks up the matching rate for R1. Later we may have additional * scaling terms for the specified voxel. */ double Stoich::getR1( const Eref& e ) const { return rates_[ convertIdToReacIndex( e.id() ) ]->getR1(); } double Stoich::getR1offset1( const Eref& e ) const { return rates_[ convertIdToReacIndex( e.id() ) + 1 ]->getR1(); } double Stoich::getR1offset2( const Eref& e ) const { return rates_[ convertIdToReacIndex( e.id() ) + 2 ]->getR1(); } /** * Looks up the matching rate for R2. Later we may have additional * scaling terms for the specified voxel. */ double Stoich::getR2( const Eref& e ) const { return rates_[ convertIdToReacIndex( e.id() ) ]->getR2(); } void Stoich::setFunctionExpr( const Eref& e, string expr ) { unsigned int index = convertIdToReacIndex( e.id() ); FuncRate* fr = 0; if ( index != ~0U ) fr = dynamic_cast< FuncRate* >( rates_[index] ); if ( fr ) { fr->setExpr( expr ); } else { index = convertIdToFuncIndex( e.id() ); if ( index != ~0U ) { FuncTerm *ft = dynamic_cast< FuncTerm* >( funcs_[index] ); if ( ft ) { ft->setExpr( expr ); return; } } cout << "Warning: Stoich::setFunctionExpr( " << e.id().path() << ", " << expr << " ): func not found"; } } ///////////////////////////////////////////////////////////////////// SpeciesId Stoich::getSpecies( unsigned int poolIndex ) const { return species_[ poolIndex ]; } void Stoich::setSpecies( unsigned int poolIndex, SpeciesId s ) { species_[ poolIndex ] = s; } // for debugging. void Stoich::print() const { N_.print(); } // for debugging void Stoich::printRates() const { for ( vector< Id >::const_iterator i = reacVec_.begin(); i != reacVec_.end(); ++i ) { double Kf = Field< double >::get( *i, "Kf"); double Kb = Field< double >::get( *i, "Kb"); double kf = Field< double >::get( *i, "kf"); double kb = Field< double >::get( *i, "kb"); cout << "Id=" << *i << ", (Kf,Kb) = (" << Kf << ", " << Kb << "), (kf, kb) = (" << kf << ", " << kb << ")\n"; } } ///////////////////////////////////////////////////////////////////// const vector< Id >& Stoich::getOffSolverPools() const { return offSolverPoolVec_; } vector< Id > Stoich::getOffSolverCompts() const { vector< Id > ret; for ( map< Id, vector< Id > >::const_iterator i = offSolverPoolMap_.begin(); i != offSolverPoolMap_.end(); ++i ) ret.push_back( i->first ); return ret; } const vector< Id >& Stoich::offSolverPoolMap( Id compt ) const { static vector< Id > blank( 0 ); map< Id, vector < Id > >::const_iterator i = offSolverPoolMap_.find( compt ); if ( i != offSolverPoolMap_.end() ) return i->second; return blank; } ///////////////////////////////////////////////////////////////////// // Numeric funcs. These are in Stoich because the rate terms are here. ///////////////////////////////////////////////////////////////////// // s is the array of pools, S_[meshIndex][0] void Stoich::updateFuncs( double* s, double t ) const { for ( vector< FuncTerm* >::const_iterator i = funcs_.begin(); i != funcs_.end(); ++i ) { if ( *i ) { (*i)->evalPool( s, t ); // s holds arguments and also result location } } } /** * updateJunctionRates: * Updates the rates for cross-compartment reactions. These are located * at the end of the rates_ vector, and are directly indexed by the * reacTerms. void Stoich::updateJunctionRates( const double* s, const vector< unsigned int >& reacTerms, double* yprime ) { for ( vector< unsigned int >::const_iterator i = reacTerms.begin(); i != reacTerms.end(); ++i ) { assert( *i < rates_[0].size() ); *yprime++ += (*rates_[0][*i])( s ); } } */ void Stoich::updateRatesAfterRemesh() { for ( vector< Id >::iterator i = reacVec_.begin(); i != reacVec_.end(); ++i ) { double Kf = Field< double >::get( *i, "Kf"); double Kb = Field< double >::get( *i, "Kb"); setReacKf( i->eref(), Kf ); setReacKb( i->eref(), Kb ); } vector< Id >::iterator i; for (i = offSolverReacVec_.begin(); i != offSolverReacVec_.end(); ++i) { assert ( i->element()->cinfo()->isA( "ReacBase" ) ); double Kf = Field< double >::get( *i, "Kf"); double Kb = Field< double >::get( *i, "Kb"); setReacKf( i->eref(), Kf ); setReacKb( i->eref(), Kb ); } for (i = offSolverEnzVec_.begin(); i != offSolverEnzVec_.end(); ++i) { assert ( i->element()->cinfo()->isA( "CplxEnzBase" ) ); double concK1 = Field< double >::get( *i, "concK1"); double k3 = Field< double >::get( *i, "k3"); double k2 = Field< double >::get( *i, "k2"); setEnzK3( i->eref(), k3 ); setEnzK2( i->eref(), k2 ); setEnzK1( i->eref(), concK1 ); } for (i=offSolverMMenzVec_.begin(); i != offSolverMMenzVec_.end(); ++i) { assert( i->element()->cinfo()->isA( "MMEnzBase" ) ); double Km = Field< double >::get( *i, "Km"); double kcat = Field< double >::get( *i, "kcat"); setMMenzKm( i->eref(), Km ); setMMenzKcat( i->eref(), kcat ); } } /* unsigned int Stoich::indexOfMatchingVolume( double vol ) const { assert( rates_.size() == uniqueVols_.size() ); assert( rates_.size() > 0 ); if ( rates_.size() == 1 && uniqueVols_[0] < 0 ) { return 0; } double bigVol = uniqueVols_[0]; for ( unsigned int i = 0; i < uniqueVols_.size(); ++i ) { if ( doubleEq( vol/bigVol, uniqueVols_[i]/bigVol ) ) return i; } assert( 0 ); return 0; } */ ///////////////////////////////////////////////////////////////////////// // Functions for resizing of specified voxels ///////////////////////////////////////////////////////////////////////// void Stoich::scaleBufsAndRates( unsigned int index, double volScale ) { if ( !kinterface_ || status_ != 0 ) return; kinterface_->pools( index )->scaleVolsBufsRates( volScale, this ); }