/** * Member function updating the neuron state by integrating the ODE. * @param origin * @param from * @param to */ void nest::aeif_cond_alpha_RK5::update( Time const& origin, const long from, const long to ) // proceed in time { assert( to >= 0 && ( delay ) from < kernel().connection_manager.get_min_delay() ); assert( from < to ); assert( State_::V_M == 0 ); for ( long lag = from; lag < to; ++lag ) // proceed by stepsize B_.step_ { double t = 0.0; // internal time of the integration period if ( S_.r_ > 0 ) // decrease remaining refractory steps if non-zero { --S_.r_; } // numerical integration with adaptive step size control: // ------------------------------------------------------ // The numerical integration of the model equations is performed by // a Dormand-Prince method (5th order Runge-Kutta method with // adaptive stepsize control) as desribed in William H. Press et // al., "Adaptive Stepsize Control for Runge-Kutta", Chapter 17.2 // in Numerical Recipes (3rd edition, 2007), 910-914. The solver // itself performs only a single NUMERICAL integration step, // starting from t and of size B_.IntegrationStep_ (bounded by // step); the while-loop ensures integration over the whole // SIMULATION step (0, step] of size B_.step_ if more than one // integration step is needed due to a small integration stepsize; // note that (t+IntegrationStep > step) leads to integration over // (t, step] and afterwards setting t to step, but it does not // enforce setting IntegrationStep to step-t; this is of advantage // for a consistent and efficient integration across subsequent // simulation intervals. double& h = B_.IntegrationStep_; // numerical integration step double& tend = B_.step_; // end of simulation step const double& MAXERR = P_.MAXERR; // maximum error const double& HMIN = P_.HMIN; // minimal integration step double err; double t_return = 0.0; while ( t < B_.step_ ) // while not yet reached end of simulation step { bool done = false; do { if ( tend - t < h ) // stop integration at end of simulation step { h = tend - t; } t_return = t + h; // update t // k1 = f(told, y) ( this->*( V_.model_dynamics ) )( S_.y_, S_.k1 ); // k2 = f(told + h/5, y + h*k1 / 5) for ( int i = 0; i < S_.STATE_VEC_SIZE; ++i ) { S_.yin[ i ] = S_.y_[ i ] + h * S_.k1[ i ] / 5.0; } ( this->*( V_.model_dynamics ) )( S_.yin, S_.k2 ); // k3 = f(told + 3/10*h, y + 3/40*h*k1 + 9/40*h*k2) for ( int i = 0; i < S_.STATE_VEC_SIZE; ++i ) { S_.yin[ i ] = S_.y_[ i ] + h * ( 3.0 / 40.0 * S_.k1[ i ] + 9.0 / 40.0 * S_.k2[ i ] ); } ( this->*( V_.model_dynamics ) )( S_.yin, S_.k3 ); // k4 for ( int i = 0; i < S_.STATE_VEC_SIZE; ++i ) { S_.yin[ i ] = S_.y_[ i ] + h * ( 44.0 / 45.0 * S_.k1[ i ] - 56.0 / 15.0 * S_.k2[ i ] + 32.0 / 9.0 * S_.k3[ i ] ); } ( this->*( V_.model_dynamics ) )( S_.yin, S_.k4 ); // k5 for ( int i = 0; i < S_.STATE_VEC_SIZE; ++i ) { S_.yin[ i ] = S_.y_[ i ] + h * ( 19372.0 / 6561.0 * S_.k1[ i ] - 25360.0 / 2187.0 * S_.k2[ i ] + 64448.0 / 6561.0 * S_.k3[ i ] - 212.0 / 729.0 * S_.k4[ i ] ); } ( this->*( V_.model_dynamics ) )( S_.yin, S_.k5 ); // k6 for ( int i = 0; i < S_.STATE_VEC_SIZE; ++i ) { S_.yin[ i ] = S_.y_[ i ] + h * ( 9017.0 / 3168.0 * S_.k1[ i ] - 355.0 / 33.0 * S_.k2[ i ] + 46732.0 / 5247.0 * S_.k3[ i ] + 49.0 / 176.0 * S_.k4[ i ] - 5103.0 / 18656.0 * S_.k5[ i ] ); } ( this->*( V_.model_dynamics ) )( S_.yin, S_.k6 ); // 5th order for ( int i = 0; i < S_.STATE_VEC_SIZE; ++i ) { S_.ynew[ i ] = S_.y_[ i ] + h * ( 35.0 / 384.0 * S_.k1[ i ] + 500.0 / 1113.0 * S_.k3[ i ] + 125.0 / 192.0 * S_.k4[ i ] - 2187.0 / 6784.0 * S_.k5[ i ] + 11.0 / 84.0 * S_.k6[ i ] ); } ( this->*( V_.model_dynamics ) )( S_.ynew, S_.k7 ); // 4th order for ( int i = 0; i < S_.STATE_VEC_SIZE; ++i ) { S_.yref[ i ] = S_.y_[ i ] + h * ( 5179.0 / 57600.0 * S_.k1[ i ] + 7571.0 / 16695.0 * S_.k3[ i ] + 393.0 / 640.0 * S_.k4[ i ] - 92097.0 / 339200.0 * S_.k5[ i ] + 187.0 / 2100.0 * S_.k6[ i ] + 1.0 / 40.0 * S_.k7[ i ] ); } err = std::fabs( S_.ynew[ 0 ] - S_.yref[ 0 ] ) / MAXERR + 1.0e-200; // error estimate, // based on different orders for stepsize prediction. Small value added // to prevent err==0 // The following flag 'done' is needed to ensure that we accept the // result for h<=HMIN, irrespective of the error. (See below) done = ( h <= HMIN ); // Always exit loop if h was <=HMIN already // prediction of next integration stepsize. This step may result in a // stepsize below HMIN. // If this happens, we must // 1. set the stepsize to HMIN // 2. compute the result and accept it irrespective of the error, // because we cannot decrease the stepsize any further. // the 'done' flag, computed above ensure that the loop is terminated // after the result was computed. h *= 0.98 * std::pow( 1.0 / err, 1.0 / 5.0 ); h = std::max( h, HMIN ); } while ( ( err > 1.0 ) and ( not done ) ); // reject step if err > 1 for ( unsigned int i = 0; i < S_.STATE_VEC_SIZE; ++i ) { S_.y_[ i ] = S_.ynew[ i ]; // pass updated values } t = t_return; // check for unreasonable values; we allow V_M to explode if ( S_.y_[ State_::V_M ] < -1e3 || S_.y_[ State_::W ] < -1e6 || S_.y_[ State_::W ] > 1e6 ) { throw NumericalInstability( get_name() ); } // spikes are handled inside the while-loop // due to spike-driven adaptation if ( S_.r_ > 0 ) // if neuron is still in refractory period { S_.y_[ State_::V_M ] = P_.V_reset_; // clamp it to V_reset } else if ( S_.y_[ State_::V_M ] >= V_.V_peak ) // V_m >= V_peak: spike { S_.y_[ State_::V_M ] = P_.V_reset_; S_.y_[ State_::W ] += P_.b; // spike-driven adaptation S_.r_ = V_.refractory_counts_; // initialize refractory steps with // refractory period set_spiketime( Time::step( origin.get_steps() + lag + 1 ) ); SpikeEvent se; kernel().event_delivery_manager.send( *this, se, lag ); } } // while S_.y_[ State_::DG_EXC ] += B_.spike_exc_.get_value( lag ) * V_.g0_ex_; // add incoming spikes S_.y_[ State_::DG_INH ] += B_.spike_inh_.get_value( lag ) * V_.g0_in_; // set new input current B_.I_stim_ = B_.currents_.get_value( lag ); // log state data B_.logger_.record_data( origin.get_steps() + lag ); } // for-loop } // function update()
void nest::aeif_cond_exp::update(const Time &origin, const long_t from, const long_t to) { assert ( to >= 0 && (delay) from < Scheduler::get_min_delay() ); assert ( from < to ); assert ( State_::V_M == 0 ); for ( long_t lag = from; lag < to; ++lag ) { double t = 0.0; if ( S_.r_ > 0 ) --S_.r_; // numerical integration with adaptive step size control: // ------------------------------------------------------ // gsl_odeiv_evolve_apply performs only a single numerical // integration step, starting from t and bounded by step; // the while-loop ensures integration over the whole simulation // step (0, step] if more than one integration step is needed due // to a small integration step size; // note that (t+IntegrationStep > step) leads to integration over // (t, step] and afterwards setting t to step, but it does not // enforce setting IntegrationStep to step-t while ( t < B_.step_ ) { const int status = gsl_odeiv_evolve_apply(B_.e_, B_.c_, B_.s_, &B_.sys_, // system of ODE &t, // from t B_.step_, // to t <= step &B_.IntegrationStep_, // integration step size S_.y_); // neuronal state if ( status != GSL_SUCCESS ) throw GSLSolverFailure(get_name(), status); // check for unreasonable values; we allow V_M to explode if ( S_.y_[State_::V_M] < -1e3 || S_.y_[State_::W ] < -1e6 || S_.y_[State_::W] > 1e6 ) throw NumericalInstability(get_name()); // spikes are handled inside the while-loop // due to spike-driven adaptation if ( S_.r_ > 0 ) S_.y_[State_::V_M] = P_.V_reset_; else if ( S_.y_[State_::V_M] >= P_.V_peak_ ) { S_.y_[State_::V_M] = P_.V_reset_; S_.y_[State_::W] += P_.b; // spike-driven adaptation S_.r_ = V_.RefractoryCounts_; set_spiketime(Time::step(origin.get_steps() + lag + 1)); SpikeEvent se; network()->send(*this, se, lag); } } S_.y_[State_::G_EXC] += B_.spike_exc_.get_value(lag); S_.y_[State_::G_INH] += B_.spike_inh_.get_value(lag); // set new input current B_.I_stim_ = B_.currents_.get_value(lag); // log state data B_.logger_.record_data(origin.get_steps() + lag); } }
void nest::aeif_psc_alpha::update( Time const& origin, const long from, const long to ) { assert( to >= 0 && ( delay ) from < kernel().connection_manager.get_min_delay() ); assert( from < to ); assert( State_::V_M == 0 ); for ( long lag = from; lag < to; ++lag ) { double t = 0.0; // numerical integration with adaptive step size control: // ------------------------------------------------------ // gsl_odeiv_evolve_apply performs only a single numerical // integration step, starting from t and bounded by step; // the while-loop ensures integration over the whole simulation // step (0, step] if more than one integration step is needed due // to a small integration step size; // note that (t+IntegrationStep > step) leads to integration over // (t, step] and afterwards setting t to step, but it does not // enforce setting IntegrationStep to step-t; this is of advantage // for a consistent and efficient integration across subsequent // simulation intervals while ( t < B_.step_ ) { const int status = gsl_odeiv_evolve_apply( B_.e_, B_.c_, B_.s_, &B_.sys_, // system of ODE &t, // from t B_.step_, // to t <= step &B_.IntegrationStep_, // integration step size S_.y_ ); // neuronal state if ( status != GSL_SUCCESS ) { throw GSLSolverFailure( get_name(), status ); } // check for unreasonable values; we allow V_M to explode if ( S_.y_[ State_::V_M ] < -1e3 || S_.y_[ State_::W ] < -1e6 || S_.y_[ State_::W ] > 1e6 ) { throw NumericalInstability( get_name() ); } // spikes are handled inside the while-loop // due to spike-driven adaptation if ( S_.r_ > 0 ) { S_.y_[ State_::V_M ] = P_.V_reset_; } else if ( S_.y_[ State_::V_M ] >= V_.V_peak ) { S_.y_[ State_::V_M ] = P_.V_reset_; S_.y_[ State_::W ] += P_.b; // spike-driven adaptation /* Initialize refractory step counter. * - We need to add 1 to compensate for count-down immediately after * while loop. * - If neuron has no refractory time, set to 0 to avoid refractory * artifact inside while loop. */ S_.r_ = V_.refractory_counts_ > 0 ? V_.refractory_counts_ + 1 : 0; set_spiketime( Time::step( origin.get_steps() + lag + 1 ) ); SpikeEvent se; kernel().event_delivery_manager.send( *this, se, lag ); } } // decrement refractory count if ( S_.r_ > 0 ) { --S_.r_; } // apply spikes S_.y_[ State_::DI_EXC ] += B_.spike_exc_.get_value( lag ) * V_.i0_ex_; S_.y_[ State_::DI_INH ] += B_.spike_inh_.get_value( lag ) * V_.i0_in_; // set new input current B_.I_stim_ = B_.currents_.get_value( lag ); // log state data B_.logger_.record_data( origin.get_steps() + lag ); } }