/********************************************************************** ** This program is part of 'MOOSE', the ** Messaging Object Oriented Simulation Environment. ** Copyright (C) 2003-2013 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 "../shell/Shell.h" #include "../randnum/randnum.h" #include "CompartmentBase.h" #include "../utility/testing_macros.hpp" #include "Compartment.h" /* #include "HHGate.h" #include "ChanBase.h" #include "HHChannel.h" */ extern void testCompartment(); // Defined in Compartment.cpp extern void testCompartmentProcess(); // Defined in Compartment.cpp extern void testMarkovRateTable(); //Defined in MarkovRateTable.cpp extern void testVectorTable(); //Defined in VectorTable.cpp /* extern void testSpikeGen(); // Defined in SpikeGen.cpp extern void testCaConc(); // Defined in CaConc.cpp extern void testNernst(); // Defined in Nernst.cpp extern void testMarkovSolverBase(); //Defined in MarkovSolverBase.cpp extern void testMarkovSolver(); //Defined in MarkovSolver.cpp */ #ifdef DO_UNIT_TESTS // Use a larger value of runsteps when benchmarking void testIntFireNetwork( unsigned int runsteps = 5 ) { static const double thresh = 0.8; static const double Vmax = 1.0; static const double refractoryPeriod = 0.4; static const double weightMax = 0.02; static const double timestep = 0.2; static const double delayMax = 4; static const double delayMin = 0; static const double connectionProbability = 0.1; static const unsigned int NUM_TOT_SYN = 104576; unsigned int size = 1024; string arg; Eref sheller( Id().eref() ); Shell* shell = reinterpret_cast< Shell* >( sheller.data() ); Id fire = shell->doCreate( "IntFire", Id(), "network", size ); assert( fire.element()->getName() == "network" ); Id i2 = shell->doCreate( "SimpleSynHandler", fire, "syns", size ); assert( i2.element()->getName() == "syns" ); Id synId( i2.value() + 1 ); Element* syn = synId.element(); assert( syn->getName() == "synapse" ); DataId di( 1 ); // DataId( data, field ) Eref syne( syn, di ); ObjId mid = shell->doAddMsg( "Sparse", fire, "spikeOut", ObjId( synId, 0 ), "addSpike" ); SetGet2< double, long >::set( mid, "setRandomConnectivity", connectionProbability, 5489UL ); mid = shell->doAddMsg( "OneToOne", i2, "activationOut", fire, "activation" ); assert( !mid.bad() ); unsigned int nd = syn->totNumLocalField(); if ( Shell::numNodes() == 1 ) assert( nd == NUM_TOT_SYN ); else if ( Shell::numNodes() == 2 ) assert( nd == 52446 ); else if ( Shell::numNodes() == 3 ) //assert( nd == 34969 ); assert( nd == 35087 ); else if ( Shell::numNodes() == 4 ) assert( nd == 26381 ); ////////////////////////////////////////////////////////////////// // Checking access to message info through SparseMsg on many nodes. ////////////////////////////////////////////////////////////////// vector< ObjId > tgts; vector< string > funcs; ObjId oi( fire, 123 ); tgts = LookupField< string, vector< ObjId > >:: get( oi, "msgDests", "spikeOut" ); funcs = LookupField< string, vector< string > >:: get( oi, "msgDestFunctions", "spikeOut" ); assert( tgts.size() == funcs.size() ); /* assert( tgts.size() == 116 ); assert( tgts[0] == ObjId( synId, 20, 11 ) ); assert( tgts[1] == ObjId( synId, 27, 15 ) ); assert( tgts[2] == ObjId( synId, 57, 14 ) ); assert( tgts[90] == ObjId( synId, 788, 15 ) ); assert( tgts[91] == ObjId( synId, 792, 12 ) ); assert( tgts[92] == ObjId( synId, 801, 17 ) ); */ for ( unsigned int i = 0; i < funcs.size(); ++i ) assert( funcs[i] == "addSpike" ); ////////////////////////////////////////////////////////////////// // Here we have an interesting problem. The mtRand might be called // by multiple threads if the above Set call is not complete. vector< double > origVm( size, 0.0 ); for ( unsigned int i = 0; i < size; ++i ) origVm[i] = mtrand() * Vmax; double origVm100 = origVm[100]; double origVm900 = origVm[900]; vector< double > temp; temp.clear(); temp.resize( size, thresh ); bool ret = Field< double >::setVec( fire, "thresh", temp ); assert( ret ); temp.clear(); temp.resize( size, refractoryPeriod ); ret = Field< double >::setVec( fire, "refractoryPeriod", temp ); assert( ret ); // cout << Shell::myNode() << ": fieldSize = " << fieldSize << endl; vector< unsigned int > numSynVec; Field< unsigned int >::getVec( i2, "numSynapses", numSynVec ); assert ( numSynVec.size() == size ); unsigned int numTotSyn = 0; for ( unsigned int i = 0; i < size; ++i ) numTotSyn += numSynVec[i]; assert( numTotSyn == NUM_TOT_SYN ); vector< vector< double > > weight( size ); for ( unsigned int i = 0; i < size; ++i ) { weight[i].resize( numSynVec[i], 0.0 ); vector< double > delay( numSynVec[i], 0.0 ); for ( unsigned int j = 0; j < numSynVec[i]; ++j ) { weight[i][ j ] = mtrand() * weightMax; delay[ j ] = delayMin + mtrand() * ( delayMax - delayMin ); } ret = Field< double >:: setVec( ObjId( synId, i ), "weight", weight[i] ); assert( ret ); ret = Field< double >::setVec( ObjId( synId, i ), "delay", delay ); assert( ret ); } for ( unsigned int i = 0; i < size; ++i ) { vector< double > retVec(0); Field< double >::getVec( ObjId( synId, i ), "weight", retVec ); assert( retVec.size() == numSynVec[i] ); for ( unsigned int j = 0; j < numSynVec[i]; ++j ) { ASSERT_DOUBLE_EQ("", retVec[j], weight[i][j] ); } } // We have to have the SynHandlers called before the network of // IntFires since the 'activation' message must be delivered within // the same timestep. shell->doUseClock("/network/syns", "process", 0 ); shell->doUseClock("/network", "process", 1 ); shell->doSetClock( 0, timestep ); shell->doSetClock( 1, timestep ); shell->doSetClock( 9, timestep ); shell->doReinit(); ret = Field< double >::setVec( fire, "Vm", origVm ); assert( ret ); double retVm100 = Field< double >::get( ObjId( fire, 100 ), "Vm" ); double retVm900 = Field< double >::get( ObjId( fire, 900 ), "Vm" ); assert( fabs( retVm100 - origVm100 ) < 1e-6 ); assert( fabs( retVm900 - origVm900 ) < 1e-6 ); shell->doStart( static_cast< double >( timestep * runsteps) + 0.0 ); if ( runsteps == 5 ) // default for unit tests, others are benchmarks { retVm100 = Field< double >::get( ObjId( fire, 100 ), "Vm" ); double retVm101 = Field< double >::get( ObjId( fire, 101 ), "Vm" ); double retVm102 = Field< double >::get( ObjId( fire, 102 ), "Vm" ); double retVm99 = Field< double >::get( ObjId( fire, 99 ), "Vm" ); retVm900 = Field< double >::get( ObjId( fire, 900 ), "Vm" ); double retVm901 = Field< double >::get( ObjId( fire, 901 ), "Vm" ); double retVm902 = Field< double >::get( ObjId( fire, 902 ), "Vm" ); /* ASSERT_DOUBLE_EQ("", retVm100, 0.00734036 ); ASSERT_DOUBLE_EQ("", retVm101, 0.246818 ); ASSERT_DOUBLE_EQ("", retVm102, 0.200087 ); ASSERT_DOUBLE_EQ("", retVm99, 0.0095779083 ); ASSERT_DOUBLE_EQ("", retVm900, 0.1150573482 ); ASSERT_DOUBLE_EQ("", retVm901, 0.289321534 ); ASSERT_DOUBLE_EQ("", retVm902, 0.01011172486 ); ASSERT_DOUBLE_EQ("", retVm100, 0.008593194687366486 ); ASSERT_DOUBLE_EQ("", retVm101, 0.24931678857743744 ); ASSERT_DOUBLE_EQ("", retVm102, 0.19668269662484533 ); ASSERT_DOUBLE_EQ("", retVm99, 0.00701607616202429 ); ASSERT_DOUBLE_EQ("", retVm900, 0.12097053045094018 ); ASSERT_DOUBLE_EQ("", retVm901, 0.2902593120492995 ); ASSERT_DOUBLE_EQ("", retVm902, 0.00237157280699805 ); ASSERT_DOUBLE_EQ("", retVm100, 0.015766608829826119 ); ASSERT_DOUBLE_EQ("", retVm101, 0.24405557875013356 ); ASSERT_DOUBLE_EQ("", retVm102, 0.20878261213859917 ); ASSERT_DOUBLE_EQ("", retVm99, 0.0081746848675747306 ); ASSERT_DOUBLE_EQ("", retVm900, 0.12525297735741736 ); ASSERT_DOUBLE_EQ("", retVm901, 0.28303358631241327 ); ASSERT_DOUBLE_EQ("", retVm902, 0.0096374021108587178 ); */ ASSERT_DOUBLE_EQ("", retVm100, 0.069517018453329804 ); ASSERT_DOUBLE_EQ("", retVm101, 0.32823493598699577 ); ASSERT_DOUBLE_EQ("", retVm102, 0.35036493874475361 ); ASSERT_DOUBLE_EQ("", retVm99, 0.04087358817787364 ); ASSERT_DOUBLE_EQ("", retVm900, 0.26414663635984065 ); ASSERT_DOUBLE_EQ("", retVm901, 0.39864519810259352 ); ASSERT_DOUBLE_EQ("", retVm902, 0.04818717439429359 ); } /* cout << "testIntFireNetwork: Vm100 = " << retVm100 << ", " << retVm101 << ", " << retVm102 << ", " << retVm99 << ", " << Vm100 << endl; cout << "Vm900 = " << retVm900 << ", "<< retVm901 << ", " << retVm902 << ", " << Vm900 << endl; */ cout << "." << flush; shell->doDelete( fire ); } static const double EREST = -0.07; #if 0 void testHHGateCreation() { Shell* shell = reinterpret_cast< Shell* >( ObjId( Id(), 0 ).data() ); vector< int > dims( 1, 1 ); Id nid = shell->doCreate( "Neutral", Id(), "n", dims ); Id comptId = shell->doCreate( "Compartment", nid, "compt", dims ); Id chanId = shell->doCreate( "HHChannel", nid, "Na", dims ); MsgId mid = shell->doAddMsg( "Single", ObjId( comptId ), "channel", ObjId( chanId ), "channel" ); assert( mid != Msg::bad ); // Check gate construction and removal when powers are assigned vector< Id > kids; HHChannel* chan = reinterpret_cast< HHChannel* >(chanId.eref().data()); assert( chan->xGate_ == 0 ); assert( chan->yGate_ == 0 ); assert( chan->zGate_ == 0 ); kids = Field< vector< Id > >::get( chanId, "children" ); assert( kids.size() == 3 ); assert( kids[0] == Id( chanId.value() + 1 ) ); assert( kids[1] == Id( chanId.value() + 2 ) ); assert( kids[2] == Id( chanId.value() + 3 ) ); assert( kids[0]()->dataHandler()->localEntries() == 0 ); assert( kids[1]()->dataHandler()->localEntries() == 0 ); assert( kids[2]()->dataHandler()->localEntries() == 0 ); Field< double >::set( chanId, "Xpower", 1 ); assert( chan->xGate_ != 0 ); assert( chan->yGate_ == 0 ); assert( chan->zGate_ == 0 ); kids = Field< vector< Id > >::get( chanId, "children" ); assert( kids.size() == 3 ); assert( kids[0]()->dataHandler()->localEntries() == 1 ); assert( kids[1]()->dataHandler()->localEntries() == 0 ); assert( kids[2]()->dataHandler()->localEntries() == 0 ); // Read the size of the gateIds. Field< double >::set( chanId, "Xpower", 2 ); assert( chan->xGate_ != 0 ); assert( chan->yGate_ == 0 ); assert( chan->zGate_ == 0 ); assert( kids[0]()->dataHandler()->localEntries() == 1 ); assert( kids[1]()->dataHandler()->localEntries() == 0 ); assert( kids[2]()->dataHandler()->localEntries() == 0 ); Field< double >::set( chanId, "Xpower", 0 ); assert( chan->xGate_ == 0 ); assert( chan->yGate_ == 0 ); assert( chan->zGate_ == 0 ); kids = Field< vector< Id > >::get( chanId, "children" ); assert( kids.size() == 3 ); // Even though gate was deleted, its Id is intact. assert( kids[0] == Id( chanId.value() + 1 ) ); assert( kids[0]()->dataHandler()->localEntries() == 0 ); assert( kids[1]()->dataHandler()->localEntries() == 0 ); assert( kids[2]()->dataHandler()->localEntries() == 0 ); Field< double >::set( chanId, "Xpower", 2 ); assert( chan->xGate_ != 0 ); assert( chan->yGate_ == 0 ); assert( chan->zGate_ == 0 ); kids = Field< vector< Id > >::get( chanId, "children" ); assert( kids.size() == 3 ); // New gate was created but has orig Id assert( kids[0] == Id( chanId.value() + 1 ) ); assert( kids[0]()->dataHandler()->localEntries() == 1 ); assert( kids[1]()->dataHandler()->localEntries() == 0 ); assert( kids[2]()->dataHandler()->localEntries() == 0 ); Field< double >::set( chanId, "Ypower", 3 ); assert( chan->xGate_ != 0 ); assert( chan->yGate_ != 0 ); assert( chan->zGate_ == 0 ); kids = Field< vector< Id > >::get( chanId, "children" ); assert( kids.size() == 3 ); assert( kids[0]()->dataHandler()->localEntries() == 1 ); assert( kids[1]()->dataHandler()->localEntries() == 1 ); assert( kids[2]()->dataHandler()->localEntries() == 0 ); Field< double >::set( chanId, "Zpower", 1 ); assert( chan->xGate_ != 0 ); assert( chan->yGate_ != 0 ); assert( chan->zGate_ != 0 ); kids = Field< vector< Id > >::get( chanId, "children" ); assert( kids.size() == 3 ); assert( kids[0] == Id( chanId.value() + 1 ) ); assert( kids[1] == Id( chanId.value() + 2 ) ); assert( kids[2] == Id( chanId.value() + 3 ) ); assert( kids[0]()->dataHandler()->localEntries() == 1 ); assert( kids[1]()->dataHandler()->localEntries() == 1 ); assert( kids[2]()->dataHandler()->localEntries() == 1 ); shell->doDelete( nid ); cout << "." << flush; } // AP measured in millivolts wrt EREST, at a sample interval of // 100 usec static double actionPotl[] = { 0, 1.2315, 2.39872, 3.51917, 4.61015, 5.69088, 6.78432, 7.91934, 9.13413, 10.482, 12.0417, 13.9374, 16.3785, 19.7462, 24.7909, 33.0981, 47.967, 73.3833, 98.7377, 105.652, 104.663, 101.815, 97.9996, 93.5261, 88.6248, 83.4831, 78.2458, 73.0175, 67.8684, 62.8405, 57.9549, 53.217, 48.6206, 44.1488, 39.772, 35.4416, 31.0812, 26.5764, 21.7708, 16.4853, 10.6048, 4.30989, -1.60635, -5.965, -8.34834, -9.3682, -9.72711, -9.81085, -9.78371, -9.71023, -9.61556, -9.50965, -9.39655, -9.27795, -9.15458, -9.02674, -8.89458, -8.75814, -8.61746, -8.47254, -8.32341, -8.17008, -8.01258, -7.85093, -7.68517, -7.51537, -7.34157, -7.16384, -6.98227, -6.79695, -6.60796, -6.41542, -6.21944, -6.02016, -5.81769, -5.61219, -5.40381, -5.19269, -4.979, -4.76291, -4.54459, -4.32422, -4.10197, -3.87804, -3.65259, -3.42582, -3.19791, -2.96904, -2.7394, -2.50915, -2.27848, -2.04755, -1.81652, -1.58556, -1.3548, -1.12439, -0.894457, -0.665128, -0.436511, -0.208708, 0.0181928, }; // y(x) = (A + B * x) / (C + exp((x + D) / F)) // So: A = 0.1e6*(EREST+0.025), B = -0.1e6, C = -1.0, D = -(EREST+0.025) // F = -0.01 double Na_m_A( double v ) { if ( fabs( EREST + 0.025 - v ) < 1e-6 ) v += 1e-6; return 0.1e6 * ( EREST + 0.025 - v ) / ( exp ( ( EREST + 0.025 - v )/ 0.01 ) - 1.0 ); } // A = 4.0e3, B = 0, C = 0.0, D = -EREST, F = 0.018 double Na_m_B( double v ) { return 4.0e3 * exp ( ( EREST - v ) / 0.018 ); } // A = 70, B = 0, C = 0, D = -EREST, F = 0.02 double Na_h_A( double v ) { return 70.0 * exp ( ( EREST - v ) / 0.020 ); } // A = 1e3, B = 0, C = 1.0, D = -(EREST + 0.03 ), F = -0.01 double Na_h_B( double v ) { return 1.0e3 / ( exp ( ( 0.030 + EREST - v )/ 0.01 ) + 1.0 ); } // A = 1e4 * (0.01 + EREST), B = -1e4, C = -1.0, D = -(EREST + 0.01 ), F = -0.01 double K_n_A( double v ) { if ( fabs( EREST + 0.025 - v ) < 1e-6 ) v += 1e-6; return ( 1.0e4 * ( 0.01 + EREST - v ) ) / ( exp ( ( 0.01 + EREST - v ) / 0.01 ) - 1.0 ); } // A = 0.125e3, B = 0, C = 0, D = -EREST ), F = 0.08 double K_n_B( double v ) { return 0.125e3 * exp ( (EREST - v ) / 0.08 ); } void testHHGateLookup() { Id shellId = Id(); HHGate gate( shellId, Id( 1 ) ); Eref er = Id(1).eref(); Qinfo q; gate.setMin( er, &q, -2.0 ); gate.setMax( er, &q, 2.0 ); gate.setDivs( er, &q, 1 ); assert( gate.A_.size() == 2 ); assert( gate.B_.size() == 2 ); assert( gate.getDivs( er, &q ) == 1 ); ASSERT_DOUBLE_EQ("", gate.invDx_, 0.25 ); gate.A_[0] = 0; gate.A_[1] = 4; gate.lookupByInterpolation_ = 0; ASSERT_DOUBLE_EQ("", gate.lookupA( -3 ), 0 ); ASSERT_DOUBLE_EQ("", gate.lookupA( -2 ), 0 ); ASSERT_DOUBLE_EQ("", gate.lookupA( -1.5 ), 0 ); ASSERT_DOUBLE_EQ("", gate.lookupA( -1 ), 0 ); ASSERT_DOUBLE_EQ("", gate.lookupA( -0.5 ), 0 ); ASSERT_DOUBLE_EQ("", gate.lookupA( 0 ), 0 ); ASSERT_DOUBLE_EQ("", gate.lookupA( 1 ), 0 ); ASSERT_DOUBLE_EQ("", gate.lookupA( 2 ), 4 ); ASSERT_DOUBLE_EQ("", gate.lookupA( 3 ), 4 ); gate.lookupByInterpolation_ = 1; ASSERT_DOUBLE_EQ("", gate.lookupA( -3 ), 0 ); ASSERT_DOUBLE_EQ("", gate.lookupA( -2 ), 0 ); ASSERT_DOUBLE_EQ("", gate.lookupA( -1.5 ), 0.5 ); ASSERT_DOUBLE_EQ("", gate.lookupA( -1 ), 1 ); ASSERT_DOUBLE_EQ("", gate.lookupA( -0.5 ), 1.5 ); ASSERT_DOUBLE_EQ("", gate.lookupA( 0 ), 2 ); ASSERT_DOUBLE_EQ("", gate.lookupA( 1 ), 3 ); ASSERT_DOUBLE_EQ("", gate.lookupA( 2 ), 4 ); ASSERT_DOUBLE_EQ("", gate.lookupA( 3 ), 4 ); gate.B_[0] = -1; gate.B_[1] = 1; double x = 0; double y = 0; gate.lookupBoth( -3 , &x, &y ); ASSERT_DOUBLE_EQ("", x, 0 ); ASSERT_DOUBLE_EQ("", y, -1 ); gate.lookupBoth( -2 , &x, &y ); ASSERT_DOUBLE_EQ("", x, 0 ); ASSERT_DOUBLE_EQ("", y, -1 ); gate.lookupBoth( -0.5, &x, &y ); ASSERT_DOUBLE_EQ("", x, 1.5 ); ASSERT_DOUBLE_EQ("", y, -0.25 ); gate.lookupBoth( 0, &x, &y ); ASSERT_DOUBLE_EQ("", x, 2 ); ASSERT_DOUBLE_EQ("", y, 0 ); gate.lookupBoth( 1.5, &x, &y ); ASSERT_DOUBLE_EQ("", x, 3.5 ); ASSERT_DOUBLE_EQ("", y, 0.75 ); gate.lookupBoth( 100000, &x, &y ); ASSERT_DOUBLE_EQ("", x, 4 ); ASSERT_DOUBLE_EQ("", y, 1 ); cout << "." << flush; } void testHHGateSetup() { Id shellId = Id(); HHGate gate( shellId, Id( 1 ) ); Eref er = Id(1).eref(); Qinfo q; vector< double > parms; // Try out m-gate of NA. // For the alpha: // A = 0.1e6*(EREST*0.025), B = -0.1e6, C= -1, D= -(EREST+0.025), F = -0.01 // For the beta: A = 4.0e3, B = 0, C = 0.0, D = -EREST, F = 0.018 // xdivs = 100, xmin = -0.1, xmax = 0.05 unsigned int xdivs = 100; double xmin = -0.1; double xmax = 0.05; parms.push_back( 0.1e6 * ( EREST + 0.025 ) ); // A alpha parms.push_back( -0.1e6 ); // B alpha parms.push_back( -1 ); // C alpha parms.push_back( -(EREST + 0.025 ) ); // D alpha parms.push_back( -0.01 ); // F alpha parms.push_back( 4e3 ); // A beta parms.push_back( 0 ); // B beta parms.push_back( 0 ); // C beta parms.push_back( -EREST ); // D beta parms.push_back( 0.018 ); // F beta parms.push_back( xdivs ); parms.push_back( xmin ); parms.push_back( xmax ); gate.setupAlpha( er, &q, parms ); assert( gate.A_.size() == xdivs + 1 ); assert( gate.B_.size() == xdivs + 1 ); double x = xmin; double dx = (xmax - xmin) / xdivs; for ( unsigned int i = 0; i <= xdivs; ++i ) { double ma = Na_m_A( x ); double mb = Na_m_B( x ); ASSERT_DOUBLE_EQ("", gate.A_[i], ma ); ASSERT_DOUBLE_EQ("", gate.B_[i], ma + mb ); x += dx; } cout << "." << flush; } //////////////////////////////////////////////////////////////// // Check construction and result of HH squid simulation //////////////////////////////////////////////////////////////// /// Returns Id of parent neutral for squid model Id makeSquid() { Shell* shell = reinterpret_cast< Shell* >( ObjId( Id(), 0 ).data() ); vector< int > dims( 1, 1 ); Id nid = shell->doCreate( "Neutral", Id(), "n", dims ); Id comptId = shell->doCreate( "Compartment", nid, "compt", dims ); Id naId = shell->doCreate( "HHChannel", comptId, "Na", dims ); MsgId mid = shell->doAddMsg( "Single", ObjId( comptId ), "channel", ObjId( naId ), "channel" ); assert( mid != Msg::bad ); Id kId = shell->doCreate( "HHChannel", comptId, "K", dims ); mid = shell->doAddMsg( "Single", ObjId( comptId ), "channel", ObjId( kId ), "channel" ); assert( mid != Msg::bad ); ////////////////////////////////////////////////////////////////////// // set up compartment properties ////////////////////////////////////////////////////////////////////// Field< double >::set( comptId, "Cm", 0.007854e-6 ); Field< double >::set( comptId, "Ra", 7639.44e3 ); // does it matter? Field< double >::set( comptId, "Rm", 424.4e3 ); Field< double >::set( comptId, "Em", EREST + 0.010613 ); Field< double >::set( comptId, "inject", 0.1e-6 ); Field< double >::set( comptId, "initVm", EREST ); ////////////////////////////////////////////////////////////////////// // set up Na channel properties ////////////////////////////////////////////////////////////////////// Field< double >::set( naId, "Gbar", 0.94248e-3 ); Field< double >::set( naId, "Ek", EREST + 0.115 ); Field< double >::set( naId, "Xpower", 3.0 ); Field< double >::set( naId, "Ypower", 1.0 ); ////////////////////////////////////////////////////////////////////// // set up K channel properties ////////////////////////////////////////////////////////////////////// Field< double >::set( kId, "Gbar", 0.282743e-3 ); Field< double >::set( kId, "Ek", EREST - 0.012 ); Field< double >::set( kId, "Xpower", 4.0 ); ////////////////////////////////////////////////////////////////////// // set up m-gate of Na. ////////////////////////////////////////////////////////////////////// vector< Id > kids; // These are the HHGates. kids = Field< vector< Id > >::get( naId, "children" ); assert( kids.size() == 3 ); vector< double > parms; // For the alpha: // A = 0.1e6*(EREST*0.025), B = -0.1e6, C= -1, D= -(EREST+0.025), F = -0.01 // For the beta: A = 4.0e3, B = 0, C = 0.0, D = -EREST, F = 0.018 // xdivs = 100, xmin = -0.1, xmax = 0.05 unsigned int xdivs = 150; double xmin = -0.1; double xmax = 0.05; parms.push_back( 0.1e6 * ( EREST + 0.025 ) ); // A alpha parms.push_back( -0.1e6 ); // B alpha parms.push_back( -1 ); // C alpha parms.push_back( -(EREST + 0.025 ) ); // D alpha parms.push_back( -0.01 ); // F alpha parms.push_back( 4e3 ); // A beta parms.push_back( 0 ); // B beta parms.push_back( 0 ); // C beta parms.push_back( -EREST ); // D beta parms.push_back( 0.018 ); // F beta parms.push_back( xdivs ); parms.push_back( xmin ); parms.push_back( xmax ); SetGet1< vector< double > >::set( kids[0], "setupAlpha", parms ); Field< bool >::set( kids[0], "useInterpolation", 1 ); ////////////////////////////////////////////////////////////////////// // set up h-gate of NA. ////////////////////////////////////////////////////////////////////// // Alpha rates: A = 70, B = 0, C = 0, D = -EREST, F = 0.02 // Beta rates: A = 1e3, B = 0, C = 1.0, D = -(EREST + 0.03 ), F = -0.01 parms.resize( 0 ); parms.push_back( 70 ); parms.push_back( 0 ); parms.push_back( 0 ); parms.push_back( -EREST ); parms.push_back( 0.02 ); parms.push_back( 1e3 ); // A beta parms.push_back( 0 ); // B beta parms.push_back( 1 ); // C beta parms.push_back( -( EREST + 0.03 ) ); // D beta parms.push_back( -0.01 ); // F beta parms.push_back( xdivs ); parms.push_back( xmin ); parms.push_back( xmax ); SetGet1< vector< double > >::set( kids[1], "setupAlpha", parms ); Field< bool >::set( kids[1], "useInterpolation", 1 ); // Check parameters vector< double > A = Field< vector< double > >::get( kids[1], "tableA"); vector< double > B = Field< vector< double > >::get( kids[1], "tableB"); double x = xmin; double dx = (xmax - xmin) / xdivs; for ( unsigned int i = 0; i <= xdivs; ++i ) { double ha = Na_h_A( x ); double hb = Na_h_B( x ); ASSERT_DOUBLE_EQ("", A[i], ha ); ASSERT_DOUBLE_EQ("", B[i], ha + hb ); x += dx; } ////////////////////////////////////////////////////////////////////// // set up n-gate of K. ////////////////////////////////////////////////////////////////////// // Alpha rates: A = 1e4 * (0.01 + EREST), B = -1e4, C = -1.0, // D = -(EREST + 0.01 ), F = -0.01 // Beta rates: A = 0.125e3, B = 0, C = 0, D = -EREST ), F = 0.08 kids = Field< vector< Id > >::get( kId, "children" ); parms.resize( 0 ); parms.push_back( 1e4 * (0.01 + EREST) ); parms.push_back( -1e4 ); parms.push_back( -1.0 ); parms.push_back( -( EREST + 0.01 ) ); parms.push_back( -0.01 ); parms.push_back( 0.125e3 ); // A beta parms.push_back( 0 ); // B beta parms.push_back( 0 ); // C beta parms.push_back( -EREST ); // D beta parms.push_back( 0.08 ); // F beta parms.push_back( xdivs ); parms.push_back( xmin ); parms.push_back( xmax ); SetGet1< vector< double > >::set( kids[0], "setupAlpha", parms ); Field< bool >::set( kids[0], "useInterpolation", 1 ); // Check parameters A = Field< vector< double > >::get( kids[0], "tableA" ); B = Field< vector< double > >::get( kids[0], "tableB" ); x = xmin; for ( unsigned int i = 0; i <= xdivs; ++i ) { double na = K_n_A( x ); double nb = K_n_B( x ); if ( i != 40 && i != 55 ) { // Annoying near-misses due to different ways of handling // singularity. I think the method in the HHGate is better. ASSERT_DOUBLE_EQ("", A[i], na ); ASSERT_DOUBLE_EQ("", B[i], na + nb ); } x += dx; } return nid; } void testHHChannel() { Shell* shell = reinterpret_cast< Shell* >( ObjId( Id(), 0 ).data() ); vector< int > dims( 1, 1 ); Id nid = makeSquid(); Id comptId( "/n/compt" ); Id tabId = shell->doCreate( "Table", nid, "tab", dims ); MsgId mid = shell->doAddMsg( "Single", ObjId( tabId, 0 ), "requestOut", ObjId( comptId, 0 ), "get_Vm" ); assert( mid != Msg::bad ); ////////////////////////////////////////////////////////////////////// // Schedule, Reset, and run. ////////////////////////////////////////////////////////////////////// shell->doSetClock( 0, 1.0e-5 ); shell->doSetClock( 1, 1.0e-5 ); shell->doSetClock( 2, 1.0e-5 ); shell->doSetClock( 3, 1.0e-4 ); shell->doUseClock( "/n/compt", "init", 0 ); shell->doUseClock( "/n/compt", "process", 1 ); // shell->doUseClock( "/n/compt/##", "process", 2 ); shell->doUseClock( "/n/compt/Na,/n/compt/K", "process", 2 ); shell->doUseClock( "/n/tab", "process", 3 ); shell->doReinit(); shell->doReinit(); shell->doStart( 0.01 ); ////////////////////////////////////////////////////////////////////// // Check output ////////////////////////////////////////////////////////////////////// vector< double > vec = Field< vector< double > >::get( tabId, "vector" ); // assert( vec.size() == 101 ); double delta = 0; for ( unsigned int i = 0; i < 100; ++i ) { double ref = EREST + actionPotl[i] * 0.001; delta += (vec[i] - ref) * (vec[i] - ref); } assert( sqrt( delta/100 ) < 0.001 ); //////////////////////////////////////////////////////////////// // Clear it all up //////////////////////////////////////////////////////////////// shell->doDelete( nid ); cout << "." << flush; } #endif //#if 0 ///////////////////////////////////// // Markov Channel unit tests. //////////////////////////////////// //Sample current obtained from channel in Chapter 20, Sakmann & Neher, Pg. 603. //The current is sampled at intervals of 10 usec. static double sampleCurrent[] = {0.0, // This is to handle the value sent by ChanBase on reinit 0.0000000e+00, 3.0005743e-26, 1.2004594e-25, 2.7015505e-25, 4.8036751e-25, 7.5071776e-25, 1.0812402e-24, 1.4719693e-24, 1.9229394e-24, 2.4341850e-24, 3.0057404e-24, 3.6376401e-24, 4.3299183e-24, 5.0826095e-24, 5.8957481e-24, 6.7693684e-24, 7.7035046e-24, 8.6981913e-24, 9.7534627e-24, 1.0869353e-23, 1.2045897e-23, 1.3283128e-23, 1.4581082e-23, 1.5939791e-23, 1.7359292e-23, 1.8839616e-23, 2.0380801e-23, 2.1982878e-23, 2.3645883e-23, 2.5369850e-23, 2.7154813e-23, 2.9000806e-23, 3.0907863e-23, 3.2876020e-23, 3.4905309e-23, 3.6995766e-23, 3.9147423e-23, 4.1360317e-23, 4.3634480e-23, 4.5969946e-23, 4.8366751e-23, 5.0824928e-23, 5.3344511e-23, 5.5925535e-23, 5.8568033e-23, 6.1272040e-23, 6.4037589e-23, 6.6864716e-23, 6.9753453e-23, 7.2703835e-23, 7.5715897e-23, 7.8789672e-23, 8.1925194e-23, 8.5122497e-23, 8.8381616e-23, 9.1702584e-23, 9.5085435e-23, 9.8530204e-23, 1.0203692e-22, 1.0560563e-22, 1.0923636e-22, 1.1292913e-22, 1.1668400e-22, 1.2050099e-22, 1.2438013e-22, 1.2832146e-22, 1.3232502e-22, 1.3639083e-22, 1.4051894e-22, 1.4470937e-22, 1.4896215e-22, 1.5327733e-22, 1.5765494e-22, 1.6209501e-22, 1.6659757e-22, 1.7116267e-22, 1.7579032e-22, 1.8048057e-22, 1.8523345e-22, 1.9004900e-22, 1.9492724e-22, 1.9986821e-22, 2.0487195e-22, 2.0993849e-22, 2.1506786e-22, 2.2026010e-22, 2.2551524e-22, 2.3083331e-22, 2.3621436e-22, 2.4165840e-22, 2.4716548e-22, 2.5273563e-22, 2.5836888e-22, 2.6406527e-22, 2.6982483e-22, 2.7564760e-22, 2.8153360e-22, 2.8748287e-22, 2.9349545e-22, 2.9957137e-22, 3.0571067e-22 }; void testMarkovGslSolver() { Shell* shell = reinterpret_cast< Shell* >( ObjId( Id(), 0 ).data() ); unsigned size = 1; Id nid = shell->doCreate( "Neutral", Id(), "n", size ); Id comptId = shell->doCreate( "Compartment", nid, "compt", size ); Id rateTabId = shell->doCreate( "MarkovRateTable", comptId, "rateTab", size ); Id mChanId = shell->doCreate( "MarkovChannel", comptId, "mChan", size ); Id gslSolverId = shell->doCreate( "MarkovGslSolver", comptId, "gslSolver", size ); Id tabId = shell->doCreate( "Table", nid, "tab", size ); ObjId mid = shell->doAddMsg( "Single", ObjId( comptId ), "channel", ObjId( mChanId ), "channel" ); assert( !mid.bad() ); mid = shell->doAddMsg( "Single", ObjId( comptId ), "channel", ObjId( rateTabId ), "channel" ); assert( !mid.bad() ); mid = shell->doAddMsg( "Single", ObjId( gslSolverId ), "stateOut", ObjId( mChanId ), "handleState" ); assert( !mid.bad() ); mid = shell->doAddMsg("Single", ObjId( rateTabId ), "instratesOut", ObjId( gslSolverId ), "handleQ" ); mid = shell->doAddMsg( "Single", ObjId( tabId, 0 ), "requestOut", ObjId( mChanId, 0 ), "getIk" ); assert( !mid.bad() ); ////////////////////////////////////////////////////////////////////// // set up compartment properties ////////////////////////////////////////////////////////////////////// Field< double >::set( comptId, "Cm", 0.007854e-6 ); Field< double >::set( comptId, "Ra", 7639.44e3 ); // does it matter? Field< double >::set( comptId, "Rm", 424.4e3 ); Field< double >::set( comptId, "Em", -0.1 ); Field< double >::set( comptId, "inject", 0 ); Field< double >::set( comptId, "initVm", -0.1 ); ///////////////////////////////// // //Setup of Markov Channel. //This is a simple 5-state channel model taken from Chapter 20, "Single-Channel //Recording", Sakmann & Neher. //All the transition rates are constant. // //////////////////////////////// //Setting number of states, number of open states. Field< unsigned int >::set( mChanId, "numStates", 5 ); Field< unsigned int >::set( mChanId, "numOpenStates", 2 ); //Setting initial state of system. vector< double > initState; initState.push_back( 0.0 ); initState.push_back( 0.0 ); initState.push_back( 0.0 ); initState.push_back( 0.0 ); initState.push_back( 1.0 ); Field< vector< double > >::set( mChanId, "initialState", initState ); vector< string > stateLabels; stateLabels.push_back( "O1" ); stateLabels.push_back( "O2" ); stateLabels.push_back( "C1" ); stateLabels.push_back( "C2" ); stateLabels.push_back( "C3" ); Field< vector< string > >::set( mChanId, "labels", stateLabels ); vector< double > gBars; gBars.push_back( 40e-12 ); gBars.push_back( 50e-12 ); Field< vector< double > >::set( mChanId, "gbar", gBars ); //Setting up rate tables. SetGet1< unsigned int >::set( rateTabId, "init", 5 ); //Filling in values into one parameter rate table. Note that all rates here //are constant. SetGet3< unsigned int, unsigned int, double >:: set( rateTabId, "setconst", 1, 2, 0.05 ); SetGet3< unsigned int, unsigned int, double >:: set( rateTabId, "setconst", 1, 4, 3 ); SetGet3< unsigned int, unsigned int, double >:: set( rateTabId, "setconst", 2, 1, 0.00066667 ); SetGet3< unsigned int, unsigned int, double >:: set( rateTabId, "setconst", 2, 3, 0.5 ); SetGet3< unsigned int, unsigned int, double >:: set( rateTabId, "setconst", 3, 2, 15 ); SetGet3< unsigned int, unsigned int, double >:: set( rateTabId, "setconst", 3, 4, 4 ); SetGet3< unsigned int, unsigned int, double >:: set( rateTabId, "setconst", 4, 1, 0.015 ); SetGet3< unsigned int, unsigned int, double >:: set( rateTabId, "setconst", 4, 3, 0.05 ); SetGet3< unsigned int, unsigned int, double >:: set( rateTabId, "setconst", 4, 5, 2.0 ); SetGet3< unsigned int, unsigned int, double >:: set( rateTabId, "setconst", 5, 4, 0.01 ); //Setting initial state of the solver. Once this is set, the solver object //will send out messages containing the updated state to the channel object. SetGet1< vector< double > >::set( gslSolverId, "init", initState ); shell->doSetClock( 0, 1.0e-5 ); shell->doSetClock( 1, 1.0e-5 ); shell->doSetClock( 2, 1.0e-5 ); shell->doSetClock( 3, 1.0e-5 ); shell->doSetClock( 4, 1.0e-5 ); //Voltage is clamped to -100 mV in the example. Hence, we skip running the //process function. shell->doUseClock( "/n/compt", "init", 0 ); shell->doUseClock( "/n/compt", "process", 1 ); shell->doUseClock( "/n/compt/rateTab", "process", 1 ); shell->doUseClock( "/n/compt/gslSolver", "process", 1 ); shell->doUseClock( "/n/compt/mChan,/n/tab", "process", 2 ); shell->doReinit( ); shell->doReinit( ); shell->doStart( 1.0e-3 ); vector< double > vec = Field< vector< double > >::get( tabId, "vector" ); for ( unsigned i = 0; i < 101; ++i ) { if (!doubleEq( sampleCurrent[i] * 1e25, vec[i] * 1e25 )) { cout << "testMarkovGslSolver: sample=" << sampleCurrent[i]*1e25 << " calculated=" << vec[i]*1e25 << endl; } ASSERT_DOUBLE_EQ("", sampleCurrent[i] * 1e25, vec[i] * 1e25 ); } //Currents involved here are incredibly small. Scaling them up is necessary //for the doubleEq function to do its job. shell->doDelete( nid ); cout << "." << flush; } //////////////// //The testMarkovGslSolver() function includes the MarkovChannel object, but //is a rather trivial case, in that the rates are all constant. //This test simultaneously tests the MarkovChannel, MarkovGslSolver, //MarkovSolverBase and MarkovSolver classes. //This test involves simulating the 4-state NMDA channel model specified //in the following paper : //"Voltage Dependence of NMDA-Activated Macroscopic Conductances Predicted //by Single-Channel Kinetics", Craig E. Jahr and Charles F. Stevens, The Journal //of Neuroscience, 1990, 10(9), pp. 3178-3182. //It is expected that the MarkovGslSolver and the MarkovSolver objects will //give the same answer. // //Note that this is different from the NMDAChan test which involves synapses. /////////////// void testMarkovChannel() { Shell* shell = reinterpret_cast< Shell* >( ObjId( Id(), 0 ).data() ); unsigned size = 1; Id nid = shell->doCreate( "Neutral", Id(), "n", size ); Id gslComptId = shell->doCreate( "Compartment", nid, "gslCompt", size ); Id exptlComptId = shell->doCreate( "Compartment", nid, "exptlCompt", size ); Id gslRateTableId = shell->doCreate( "MarkovRateTable", gslComptId, "gslRateTable", size ); Id exptlRateTableId = shell->doCreate( "MarkovRateTable", exptlComptId, "exptlRateTable", size ); Id mChanGslId = shell->doCreate( "MarkovChannel", gslComptId, "mChanGsl", size ); Id mChanExptlId = shell->doCreate( "MarkovChannel", exptlComptId, "mChanExptl", size ); Id gslSolverId = shell->doCreate( "MarkovGslSolver", gslComptId, "gslSolver", size ); Id exptlSolverId = shell->doCreate( "MarkovSolver", exptlComptId, "exptlSolver", size ); Id gslTableId = shell->doCreate( "Table", nid, "gslTable", size ); Id exptlTableId = shell->doCreate( "Table", nid, "exptlTable", size ); Id int2dTableId = shell->doCreate( "Interpol2D", nid, "int2dTable", size ); Id vecTableId = shell->doCreate( "VectorTable", nid, "vecTable", size ); vector< double > table1d; vector< vector< double > > table2d; /////////////////////////// //Setting up the messaging. ////////////////////////// //Connecting Compartment and MarkovChannel objects. //Compartment sends Vm to MarkovChannel object. The MarkovChannel, //via its ChanBase base class, sends back the conductance and current through //it. ObjId mid = shell->doAddMsg( "Single", ObjId( gslComptId ), "channel", ObjId( mChanGslId ), "channel" ); assert( !mid.bad() ); mid = shell->doAddMsg( "Single", ObjId( exptlComptId ), "channel", ObjId( mChanExptlId ), "channel" ); assert( !mid.bad() ); //////// //Connecting up the MarkovGslSolver. /////// //Connecting Compartment and MarkovRateTable. //The MarkovRateTable's job is to send out the instantaneous rate matrix, //Q, to the solver object(s). //In order to do so, the MarkovRateTable object needs information on //Vm and ligand concentration to look up the rate from the table provided //by the user. Hence, the need of the connection to the Compartment object. //However, unlike a channel object, the MarkovRateTable object does not //return anything to the Compartment directly, and communicates only with the //solvers. mid = shell->doAddMsg( "Single", ObjId( gslComptId ), "channel", ObjId( gslRateTableId ), "channel" ); assert( !mid.bad() ); //Connecting the MarkovRateTable with the MarkovGslSolver object. //As mentioned earlier, the MarkovRateTable object sends out information //about Q to the MarkovGslSolver. The MarkovGslSolver then churns out //the state of the system for the next time step. mid = shell->doAddMsg("Single", ObjId( gslRateTableId ), "instratesOut", ObjId( gslSolverId ), "handleQ" ); //Connecting MarkovGslSolver with MarkovChannel. //The MarkovGslSolver object, upon computing the state of the channel, //sends this information to the MarkovChannel object. The MarkovChannel //object will compute the expected conductance of the channel and send //this information to the compartment. mid = shell->doAddMsg( "Single", ObjId( gslSolverId ), "stateOut", ObjId( mChanGslId ), "handleState" ); assert( !mid.bad() ); ////////// //Connecting up the MarkovSolver class. ///////// //Connecting the MarkovSolver and Compartment. //The MarkovSolver derives from the MarkovSolverBase class. //The base class need Vm and ligand concentration information to //perform lookup and interpolation on the matrix exponential lookup //tables. mid = shell->doAddMsg( "Single", ObjId( exptlComptId ), "channel", ObjId( exptlSolverId ), "channel" ); assert( !mid.bad() ); mid = shell->doAddMsg( "Single", ObjId( exptlSolverId ), "stateOut", ObjId( mChanExptlId ), "handleState" ); assert( !mid.bad() ); ///////// //Connecting up the Table objects to cross-check values. //////// //Get the current values from the GSL solver based channel. mid = shell->doAddMsg( "Single", ObjId( gslTableId ), "requestOut", ObjId( mChanGslId ), "getIk" ); assert( !mid.bad() ); //Get the current values from the matrix exponential solver based channel. mid = shell->doAddMsg( "Single", ObjId( exptlTableId ), "requestOut", ObjId( mChanExptlId ), "getIk" ); assert( !mid.bad() ); //////////////////// //Compartment properties. Identical to ones used in testHHChannel() //barring a few modifications. /////////////////// Field< double >::set( gslComptId, "Cm", 0.007854e-6 ); Field< double >::set( gslComptId, "Ra", 7639.44e3 ); // does it matter? Field< double >::set( gslComptId, "Rm", 424.4e3 ); Field< double >::set( gslComptId, "Em", EREST + 0.1 ); Field< double >::set( gslComptId, "inject", 0 ); Field< double >::set( gslComptId, "initVm", EREST ); Field< double >::set( exptlComptId, "Cm", 0.007854e-6 ); Field< double >::set( exptlComptId, "Ra", 7639.44e3 ); // does it matter? Field< double >::set( exptlComptId, "Rm", 424.4e3 ); Field< double >::set( exptlComptId, "Em", EREST + 0.1 ); Field< double >::set( exptlComptId, "inject", 0 ); Field< double >::set( exptlComptId, "initVm", EREST ); ////////////////// //Setup of rate tables. //Refer paper mentioned at the header of the unit test for more //details. ///////////////// //Number of states and open states. Field< unsigned int >::set( mChanGslId, "numStates", 4 ); Field< unsigned int >::set( mChanExptlId, "numStates", 4 ); Field< unsigned int >::set( mChanGslId, "numOpenStates", 1 ); Field< unsigned int >::set( mChanExptlId, "numOpenStates", 1 ); vector< string > stateLabels; //In the MarkovChannel class, the opening states are listed first. //This is in line with the convention followed in Chapter 20, Sakmann & //Neher. stateLabels.push_back( "O" ); //State 1. stateLabels.push_back( "B1" ); //State 2. stateLabels.push_back( "B2" ); //State 3. stateLabels.push_back( "C" ); //State 4. Field< vector< string > >::set( mChanGslId, "labels", stateLabels ); Field< vector< string > >::set( mChanExptlId, "labels", stateLabels ); //Setting up conductance value for single open state. Value chosen //is quite arbitrary. vector< double > gBar; gBar.push_back( 5.431553e-9 ); Field< vector< double > >::set( mChanGslId, "gbar", gBar ); Field< vector< double > >::set( mChanExptlId, "gbar", gBar ); //Initial state of the system. This is really an arbitrary choice. vector< double > initState; initState.push_back( 0.00 ); initState.push_back( 0.20 ); initState.push_back( 0.80 ); initState.push_back( 0.00 ); Field< vector< double > >::set( mChanGslId, "initialState", initState ); Field< vector< double > >::set( mChanExptlId, "initialState", initState ); //This initializes the GSL solver object. SetGet1< vector< double > >::set( gslSolverId, "init", initState ); //Initializing MarkovRateTable object. double v; double conc; SetGet1< unsigned int >::set( gslRateTableId, "init", 4 ); SetGet1< unsigned int >::set( exptlRateTableId, "init", 4 ); //Setting up lookup tables for the different rates. //Please note that the rates should be in sec^(-1). //Transition from "O" to "B1" i.e. r12 or a1. Field< double >::set( vecTableId, "xmin", -0.10 ); Field< double >::set( vecTableId, "xmax", 0.10 ); Field< unsigned int >::set( vecTableId, "xdivs", 200 ); v = Field< double >::get( vecTableId, "xmin" ); for ( unsigned int i = 0; i < 201; ++i ) { table1d.push_back( 1e3 * exp( -16 * v - 2.91 ) ); v += 0.001; } Field< vector< double > >::set( vecTableId, "table", table1d ); SetGet4< unsigned int, unsigned int, Id, unsigned int >::set( gslRateTableId, "set1d", 1, 2, vecTableId, 0 ); SetGet4< unsigned int, unsigned int, Id, unsigned int >::set( exptlRateTableId, "set1d", 1, 2, vecTableId, 0 ); table1d.erase( table1d.begin(), table1d.end() ); //Transition from "B1" back to O i.e. r21 or b1 v = Field< double >::get( vecTableId, "xmin" ); for ( unsigned int i = 0; i < 201; ++i ) { table1d.push_back( 1e3 * exp( 9 * v + 1.22 ) ); v += 0.001; } Field< vector< double > >::set( vecTableId, "table", table1d ); SetGet4< unsigned int, unsigned int, Id, unsigned int >::set( gslRateTableId, "set1d", 2, 1, vecTableId, 0 ); SetGet4< unsigned int, unsigned int, Id, unsigned int >::set( exptlRateTableId, "set1d", 2, 1, vecTableId, 0 ); table1d.erase( table1d.begin(), table1d.end() ); //Transition from "O" to "B2" i.e. r13 or a2 //This is actually a 2D rate. But, there is no change in Mg2+ concentration //that occurs. Hence, I create a 2D lookup table anyway but I manually //set the concentration on the rate table object anyway. Field< double >::set( gslRateTableId, "ligandConc", 24e-6 ); Field< double >::set( exptlRateTableId, "ligandConc", 24e-6 ); Field< double >::set( int2dTableId, "xmin", -0.10 ); Field< double >::set( int2dTableId, "xmax", 0.10 ); Field< double >::set( int2dTableId, "ymin", 0 ); Field< double >::set( int2dTableId, "ymax", 30e-6 ); Field< unsigned int >::set( int2dTableId, "xdivs", 200 ); Field< unsigned int >::set( int2dTableId, "ydivs", 30 ); table2d.resize( 201 ); v = Field< double >::get( int2dTableId, "xmin" ); for ( unsigned int i = 0; i < 201; ++i ) { conc = Field< double >::get( int2dTableId, "ymin" ); for ( unsigned int j = 0; j < 31; ++j ) { table2d[i].push_back( 1e3 * conc * exp( -45 * v - 6.97 ) ); conc += 1e-6; } v += 1e-3; } Field< vector< vector< double > > >::set( int2dTableId, "tableVector2D", table2d ); SetGet3< unsigned int, unsigned int, Id >::set( gslRateTableId, "set2d", 1, 3, int2dTableId ); SetGet3< unsigned int, unsigned int, Id >::set( exptlRateTableId, "set2d", 1, 3, int2dTableId ); //There is only one 2D rate, so no point manually erasing the elements. //Transition from "B2" to "O" i.e. r31 or b2 v = -0.10; for ( unsigned int i = 0; i < 201; ++i ) { table1d.push_back( 1e3 * exp( 17 * v + 0.96 ) ); v += 0.001; } Field< vector< double > >::set( vecTableId, "table", table1d ); SetGet4< unsigned int, unsigned int, Id, unsigned int >::set( gslRateTableId, "set1d", 3, 1, vecTableId, 0 ); SetGet4< unsigned int, unsigned int, Id, unsigned int >::set( exptlRateTableId, "set1d", 3, 1, vecTableId, 0 ); table1d.erase( table1d.begin(), table1d.end() ); //Transition from "O" to "C" i.e. r14 SetGet3< unsigned int, unsigned int, double >::set( gslRateTableId, "setconst", 1, 4, 1e3 * exp( -2.847 ) ); SetGet3< unsigned int, unsigned int, double >::set( exptlRateTableId, "setconst", 1, 4, 1e3 * exp( -2.847 ) ); //Transition from "B1" to "C" i.e. r24 SetGet3< unsigned int, unsigned int, double >::set( gslRateTableId, "setconst", 2, 4, 1e3 * exp( -0.693 ) ); SetGet3< unsigned int, unsigned int, double >::set( exptlRateTableId, "setconst", 2, 4, 1e3 * exp( -0.693 ) ); //Transition from "B2" to "C" i.e. r34 SetGet3< unsigned int, unsigned int, double >::set( gslRateTableId, "setconst", 3, 4, 1e3* exp( -3.101 ) ); SetGet3< unsigned int, unsigned int, double >::set( exptlRateTableId, "setconst", 3, 4, 1e3* exp( -3.101 ) ); //Once the rate tables have been set up, we can initialize the //tables in the MarkovSolver class. SetGet2< Id, double >::set( exptlSolverId, "init", exptlRateTableId, 1.0e-3 ); SetGet1< double >::set( exptlSolverId, "ligandConc", 24e-6 ); Field< vector< double > >::set( exptlSolverId, "initialState", initState ); Field< double >::set( gslSolverId, "relativeAccuracy", 1e-24 ); Field< double >::set( gslSolverId, "absoluteAccuracy", 1e-24 ); Field< double >::set( gslSolverId, "internalDt", 1e-24 ); shell->doSetClock( 0, 1.0e-3 ); shell->doSetClock( 1, 1.0e-3 ); shell->doSetClock( 2, 1.0e-3 ); shell->doSetClock( 3, 1.0e-3 ); shell->doSetClock( 4, 1.0e-3 ); shell->doSetClock( 5, 1.0e-3 ); shell->doUseClock( "/n/gslCompt,/n/exptlCompt", "init", 0 ); shell->doUseClock( "/n/gslCompt,/n/exptlCompt", "process", 1 ); shell->doUseClock( "/n/gslCompt/gslRateTable,/n/exptlCompt/exptlRateTable", "process", 2 ); shell->doUseClock( "/n/gslCompt/gslSolver,/n/exptlCompt/exptlSolver", "process", 3 ); shell->doUseClock( "/n/gslCompt/mChanGsl,/n/gslTable","process", 4 ); shell->doUseClock( "/n/exptlCompt/mChanExptl,/n/exptlTable", "process", 5 ); shell->doReinit(); // shell->doReinit();// why twice? - subha shell->doStart( 1.0 ); vector< double > gslVec = Field< vector< double > >::get( gslTableId, "vector" ); vector< double > exptlVec = Field< vector< double > >::get( exptlTableId, "vector"); assert( gslVec.size() == 1001 ); assert( exptlVec.size() == 1001 ); for ( unsigned int i = 0; i < 1001; ++i ) assert( doubleApprox( gslVec[i], exptlVec[i] ) ); shell->doDelete( nid ); cout << "." << flush; } /////////////////////////////////////////////////// // Unit tests for SynChan /////////////////////////////////////////////////// // #include "SpikeGen.h" /** * Here we set up a SynChan recieving spike inputs from two * SpikeGens. The first has a delay of 1 msec, the second of 10. * The tau of the SynChan is 1 msec. * We test for generation of peak responses at the right time, that * is 2 and 11 msec. */ void testSynChan() { Shell* shell = reinterpret_cast< Shell* >( ObjId( Id(), 0 ).data() ); Id nid = shell->doCreate( "Neutral", Id(), "n", 1 ); Id synChanId = shell->doCreate( "SynChan", nid, "synChan", 1 ); Id synHandlerId = shell->doCreate( "SimpleSynHandler", synChanId, "syns", 1 ); Id synId( synHandlerId.value() + 1 ); Id sgId1 = shell->doCreate( "SpikeGen", nid, "sg1", 1 ); Id sgId2 = shell->doCreate( "SpikeGen", nid, "sg2", 1 ); ProcInfo p; p.dt = 1.0e-4; p.currTime = 0; bool ret; assert( synId.element()->getName() == "synapse" ); ret = Field< double >::set( synChanId, "tau1", 1.0e-3 ); assert( ret ); ret = Field< double >::set( synChanId, "tau2", 1.0e-3 ); assert( ret ); ret = Field< double >::set( synChanId, "Gbar", 1.0 ); assert( ret ); // This is a hack, should really inspect msgs to automatically figure // out how many synapses are needed. ret = Field< unsigned int >::set( synHandlerId, "numSynapse", 2 ); assert( ret ); Element* syne = synId.element(); assert( syne->totNumLocalField() == 2 ); ObjId mid = shell->doAddMsg( "single", ObjId( sgId1, 0 ), "spikeOut", ObjId( synId, 0, 0 ), "addSpike" ); assert( mid != Id() ); mid = shell->doAddMsg( "single", ObjId( sgId2, 0 ), "spikeOut", ObjId( synId, 0, 1 ), "addSpike" ); assert( mid != Id() ); mid = shell->doAddMsg( "single", synHandlerId, "activationOut", synChanId, "activation" ); assert( mid != Id() ); ret = Field< double >::set( sgId1, "threshold", 0.0 ); ret = Field< double >::set( sgId1, "refractT", 1.0 ); ret = Field< bool >::set( sgId1, "edgeTriggered", 0 ); ret = Field< double >::set( sgId2, "threshold", 0.0 ); ret = Field< double >::set( sgId2, "refractT", 1.0 ); ret = Field< bool >::set( sgId2, "edgeTriggered", 0 ); ret = Field< double >::set( ObjId( synId, 0, 0 ), "weight", 1.0 ); assert( ret); ret = Field< double >::set( ObjId( synId, 0, 0 ), "delay", 0.001 ); assert( ret); ret = Field< double >::set( ObjId( synId, 0, 1 ), "weight", 1.0 ); assert( ret); ret = Field< double >::set( ObjId( synId, 0, 1 ), "delay", 0.01 ); assert( ret); double dret; dret = Field< double >::get( ObjId( synId, 0, 0 ), "weight" ); ASSERT_DOUBLE_EQ("", dret, 1.0 ); dret = Field< double >::get( ObjId( synId, 0, 0 ), "delay" ); ASSERT_DOUBLE_EQ("", dret, 0.001 ); dret = Field< double >::get( ObjId( synId, 0, 1 ), "weight" ); ASSERT_DOUBLE_EQ("", dret, 1.0 ); dret = Field< double >::get( ObjId( synId, 0, 1 ), "delay" ); ASSERT_DOUBLE_EQ("", dret, 0.01 ); dret = SetGet1< double >::set( sgId1, "Vm", 2.0 ); dret = SetGet1< double >::set( sgId2, "Vm", 2.0 ); dret = Field< double >::get( synChanId, "Gk" ); ASSERT_DOUBLE_EQ("", dret, 0.0 ); ///////////////////////////////////////////////////////////////////// shell->doSetClock( 0, 1e-4 ); shell->doSetClock( 1, 1e-4 ); shell->doSetClock( 2, 1e-4 ); // shell->doUseClock( "/n/##", "process", 0 ); // shell->doUseClock( "/n/synChan/syns,/n/sg1,/n/sg2", "process", 0 ); //It turns out that the order of setting of the spikes (sg1, sg2) //does not affect the outcome. The one thing that is critical is that //the 'process' call for the 'syns' should be before that of the //synChan. This is because the 'activation' message from the syns to //the synChan should proceed within a given timestep otherwise the //apparent arrival time of the event is delayed. shell->doUseClock( "/n/sg1,/n/sg2", "process", 0 ); shell->doUseClock( "/n/synChan/syns", "process", 1 ); shell->doUseClock( "/n/synChan", "process", 2 ); // shell->doStart( 0.001 ); shell->doReinit(); shell->doReinit(); shell->doStart( 0.001 ); dret = Field< double >::get( synChanId, "Gk" ); assert( doubleApprox( dret, 0.0 ) ); shell->doStart( 0.0005 ); dret = Field< double >::get( synChanId, "Gk" ); assert( doubleApprox( dret, 0.825 ) ); shell->doStart( 0.0005 ); dret = Field< double >::get( synChanId, "Gk" ); assert( doubleApprox( dret, 1.0 ) ); shell->doStart( 0.001 ); dret = Field< double >::get( synChanId, "Gk" ); assert( doubleApprox( dret, 0.736 ) ); shell->doStart( 0.001 ); dret = Field< double >::get( synChanId, "Gk" ); assert( doubleApprox( dret, 0.406 ) ); shell->doStart( 0.007 ); dret = Field< double >::get( synChanId, "Gk" ); // assert( doubleApprox( dret, 0.997 ) ); assert( doubleApprox( dret, 1.002 ) ); shell->doDelete( nid ); cout << "." << flush; } #if 0 void testNMDAChan() { Shell* shell = reinterpret_cast< Shell* >( ObjId( Id(), 0 ).data() ); vector< int > dims( 1, 1 ); Id nid = shell->doCreate( "Neutral", Id(), "n", dims ); Id synChanId = shell->doCreate( "NMDAChan", nid, "nmdaChan", dims ); Id synId( synChanId.value() + 1 ); Id sgId1 = shell->doCreate( "SpikeGen", nid, "sg1", dims ); ProcInfo p; p.dt = 1.0e-4; p.currTime = 0; bool ret; assert( synId()->getName() == "synapse" ); ret = Field< double >::set( synChanId, "tau1", 130.5e-3 ); assert( ret ); ret = Field< double >::set( synChanId, "tau2", 5.0e-3 ); assert( ret ); ret = Field< double >::set( synChanId, "Gbar", 1.0 ); assert( ret ); // This is a hack, should really inspect msgs to automatically figure // out how many synapses are needed. ret = Field< unsigned int >::set( synChanId, "num_synapse", 1 ); assert( ret ); Element* syne = synId(); assert( syne->dataHandler()->localEntries() == 1 ); dynamic_cast< FieldDataHandlerBase* >( syne->dataHandler() )->setNumField( synChanId.eref().data(), 1 ); assert( syne->dataHandler()->totalEntries() == 1 ); assert( syne->dataHandler()->numDimensions() == 1 ); assert( syne->dataHandler()->sizeOfDim( 0 ) == 1 ); MsgId mid = shell->doAddMsg( "single", ObjId( sgId1, DataId( 0 ) ), "spikeOut", ObjId( synId, DataId( 0 ) ), "addSpike" ); assert( mid != Id() ); ret = Field< double >::set( sgId1, "threshold", 0.0 ); ret = Field< double >::set( sgId1, "refractT", 1.0 ); ret = Field< bool >::set( sgId1, "edgeTriggered", 0 ); ret = Field< double >::set( ObjId( synId, DataId( 0 ) ), "weight", 1.0 ); assert( ret); ret = Field< double >::set( ObjId( synId, DataId( 0 ) ), "delay", 0.001 ); assert( ret); double dret; dret = Field< double >::get( ObjId( synId, DataId( 0 ) ), "weight" ); ASSERT_DOUBLE_EQ("", dret, 1.0 ); dret = Field< double >::get( ObjId( synId, DataId( 0 ) ), "delay" ); ASSERT_DOUBLE_EQ("", dret, 0.001 ); dret = SetGet1< double >::set( sgId1, "Vm", 2.0 ); dret = Field< double >::get( synChanId, "Gk" ); ASSERT_DOUBLE_EQ("", dret, 0.0 ); ///////////////////////////////////////////////////////////////////// shell->doSetClock( 0, 1e-4 ); // shell->doUseClock( "/n/##", "process", 0 ); shell->doUseClock( "/n/synChan,/n/sg1", "process", 0 ); // shell->doStart( 0.001 ); shell->doReinit(); shell->doReinit(); shell->doStart( 0.001 ); dret = Field< double >::get( synChanId, "Gk" ); assert( doubleApprox( dret, 0.0 ) ); shell->doStart( 0.0005 ); dret = Field< double >::get( synChanId, "Gk" ); cout << "Gk:" << dret << endl; assert( doubleApprox( dret, 1.0614275017053588e-07 ) ); // shell->doStart( 0.0005 ); // dret = Field< double >::get( synChanId, "Gk" ); // cout << "Gk:" << dret << endl; // assert( doubleApprox( dret, 1.0 ) ); // shell->doStart( 0.001 ); // dret = Field< double >::get( synChanId, "Gk" ); // cout << "Gk:" << dret << endl; // assert( doubleApprox( dret, 0.736 ) ); // shell->doStart( 0.001 ); // dret = Field< double >::get( synChanId, "Gk" ); // cout << "Gk:" << dret << endl; // assert( doubleApprox( dret, 0.406 ) ); // shell->doStart( 0.007 ); // dret = Field< double >::get( synChanId, "Gk" ); // cout << "Gk:" << dret << endl; // assert( doubleApprox( dret, 0.997 ) ); shell->doDelete( nid ); cout << "." << flush; } #endif static Id addCompartment( const string& name, Id neuron, Id parent, double dx, double dy, double dz, double dia ) { static Shell* shell = reinterpret_cast< Shell* >( Id().eref().data() ); double x0 = 0.0; double y0 = 0.0; double z0 = 0.0; Id compt = shell->doCreate( "Compartment", neuron, name, 1 ); if ( parent != Id() ) { ObjId mid = shell->doAddMsg( "single", parent, "axial", compt, "raxial" ); assert( mid != Id() ); x0 = Field< double >::get( parent, "x" ); y0 = Field< double >::get( parent, "y" ); z0 = Field< double >::get( parent, "z" ); } Field< double >::set( compt, "x0", x0 ); Field< double >::set( compt, "y0", y0 ); Field< double >::set( compt, "z0", z0 ); Field< double >::set( compt, "x", x0 + dx ); Field< double >::set( compt, "y", y0 + dy ); Field< double >::set( compt, "z", z0 + dz ); double length = sqrt( dx*dx + dy*dy + dz*dz ); Field< double >::set( compt, "length", length ); Field< double >::set( compt, "diameter", dia ); Field< double >::set( compt, "Rm", 1.0 / ( PI * length * dia ) ); Field< double >::set( compt, "Cm", 0.01 * ( PI * length * dia ) ); Field< double >::set( compt, "Ra", 1 * length / ( PI*0.25*dia*dia ) ); return compt; } #include "../utility/Vec.h" #include "SwcSegment.h" #include "Spine.h" #include "Neuron.h" #include "../shell/Wildcard.h" static void testNeuronBuildTree() { Shell* shell = reinterpret_cast< Shell* >( ObjId( Id(), 0 ).data() ); Id nid = shell->doCreate( "Neuron", Id(), "n", 1 ); double somaDia = 5e-6; double dendDia = 2e-6; double branchDia = 1e-6; static double len[] = { 10e-6, 100e-6, 200e-6, 500e-6 }; static double dia[] = { somaDia, dendDia, branchDia, branchDia }; Id soma = addCompartment ( "soma", nid, Id(), 10e-6, 0, 0, somaDia ); Id dend1 = addCompartment ( "dend1", nid, soma, 100e-6, 0, 0, dendDia); Id branch1 = addCompartment ( "branch1", nid, dend1, 0, 200e-6, 0, branchDia ); Id branch2 = addCompartment ( "branch2", nid, dend1, 0, -500e-6, 0, branchDia ); static double x[] = { 10e-6, 110e-6, 110e-6, 110e-6 }; static double y[] = {0, 0, 200e-6, -500e-6}; static double z[] = {0, 0, 0, 0}; SetGet0::set( nid, "buildSegmentTree" ); vector< double > e = Field< vector< double > >::get( nid, "electrotonicDistanceFromSoma" ); vector< double > g = Field< vector< double > >::get( nid, "geometricalDistanceFromSoma" ); vector< double > p = Field< vector< double > >::get( nid, "pathDistanceFromSoma" ); assert( e.size() == 4 ); ASSERT_DOUBLE_EQ("", e[0], 0.0 ); double dL = 100e-6 / sqrt( dendDia /4.0 ); ASSERT_DOUBLE_EQ("", e[1], dL ); double bL1 = dL + 200e-6 / sqrt( branchDia /4.0 ); ASSERT_DOUBLE_EQ("", e[2], bL1 ); double bL2 = dL + 500e-6 / sqrt( branchDia/4.0 ); ASSERT_DOUBLE_EQ("", e[3], bL2 ); ASSERT_DOUBLE_EQ("", p[0], 0.0 ); ASSERT_DOUBLE_EQ("", p[1], 100.0e-6 ); ASSERT_DOUBLE_EQ("", p[2], 300.0e-6 ); // 100 + 200 microns ASSERT_DOUBLE_EQ("", p[3], 600.0e-6 ); // 100 + 500 microns ASSERT_DOUBLE_EQ("", g[0], 0.0 ); ASSERT_DOUBLE_EQ("", g[1], 100.0e-6 ); ASSERT_DOUBLE_EQ("", g[2], sqrt(5.0) * 100.0e-6 ); // 100 + 200 microns ASSERT_DOUBLE_EQ("", g[3], sqrt(26.0) * 100.0e-6 ); // 100 + 500 microns ////////////////////////////////////////////////////////////////// // Here we test Neuron::evalExprForElist, which uses the muParser // Note that the wildcard list starts with the spine which is not // a compartment. So the indexing of the arrays e, p and g needs care. unsigned int nuParserNumVal = 13; vector< ObjId > elist; wildcardFind( "/n/#[ISA=Compartment]", elist ); Neuron* n = reinterpret_cast< Neuron* >( nid.eref().data() ); vector< double > val; n->evalExprForElist( elist, "p + g + L + len + dia + H(1-L)", val ); assert( val.size() == nuParserNumVal * elist.size() ); double maxP = 0.0; double maxG = 0.0; double maxL = 0.0; for ( unsigned int i = 0; i < elist.size(); ++i ) { if ( maxP < p[i] ) maxP = p[i]; if ( maxG < g[i] ) maxG = g[i]; if ( maxL < e[i] ) maxL = e[i]; } unsigned int j = 0; for ( unsigned int i = 0; i < elist.size(); ++i ) { if ( !elist[i].element()->cinfo()->isA( "CompartmentBase" ) ) continue; assert( val[i * nuParserNumVal] == p[j] + g[j] + e[j] + len[j] + dia[j] + ( 1.0 - e[j] > 0 ) ); ASSERT_DOUBLE_EQ("", val[i * nuParserNumVal + 1], p[j] ); ASSERT_DOUBLE_EQ("", val[i * nuParserNumVal + 2], g[j] ); ASSERT_DOUBLE_EQ("", val[i * nuParserNumVal + 3], e[j] ); assert( doubleEq( val[i * nuParserNumVal + 4], len[j] )); ASSERT_DOUBLE_EQ("", val[i * nuParserNumVal + 5], dia[j] ); ASSERT_DOUBLE_EQ("", val[i * nuParserNumVal + 6], maxP ); ASSERT_DOUBLE_EQ("", val[i * nuParserNumVal + 7], maxG ); ASSERT_DOUBLE_EQ("", val[i * nuParserNumVal + 8], maxL ); ASSERT_DOUBLE_EQ("", val[i * nuParserNumVal + 9], x[j] ); ASSERT_DOUBLE_EQ("", val[i * nuParserNumVal + 10], y[j] ); ASSERT_DOUBLE_EQ("", val[i * nuParserNumVal + 11], z[j] ); ASSERT_DOUBLE_EQ("", val[i * nuParserNumVal + 12], 0.0 ); j++; } ////////////////////////////////////////////////////////////////// // Here we test Neuron::makeSpacingDistrib, which uses the muParser // n->evalExprForElist( elist, "H(p-50e-6)*5e-6", val ); n->evalExprForElist( elist, "H(p-100e-6)*5e-6", val ); vector< unsigned int > seglistIndex; vector< unsigned int > elistIndex; vector< double > pos; vector< string > line; // empty, just use the default spacingDistrib=0 n->makeSpacingDistrib( elist, val, seglistIndex, elistIndex, pos, line ); // Can't do this now, it is not determinisitic. // assert( pos.size() == ((800 - 100)/5) ); // ASSERT_DOUBLE_EQ("", pos[0], 2.5e-6 ); // ASSERT_DOUBLE_EQ("", pos.back(), 500e-6 - 7.5e-6 ); assert( seglistIndex[0] == 2 ); assert( seglistIndex.back() == 3 ); assert( elistIndex[0] == 2 ); assert( elistIndex.back() == 3 ); shell->doDelete( nid ); } // This tests stuff without using the messaging. void testBiophysics() { testCompartment(); testVectorTable(); testNeuronBuildTree(); #if 0 testMarkovSolverBase(); testMarkovSolver(); testHHGateCreation(); testHHGateLookup(); testHHGateSetup(); testSpikeGen(); testCaConc(); testNernst(); #endif } // This is applicable to tests that use the messaging and scheduling. void testBiophysicsProcess() { // testSynChan(); testIntFireNetwork(); testCompartmentProcess(); // testMarkovGslSolver(); // testMarkovChannel(); #if 0 testHHChannel(); #endif } #else // ifdef DO_UNIT_TESTS void testBiophysics() { ; } void testBiophysicsProcess() { ; } void testIntFireNetwork( unsigned int unsteps = 5 ) { ; } #endif