/**
 * 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()
Example #2
0
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);
  }
}
Example #3
0
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 );
  }
}