예제 #1
0
void nest::spike_generator::Parameters_::assert_valid_spike_time_and_insert_(double t,
									     const Time& origin,
									     const Time& now)
{
  Time t_spike;
  if ( precise_times_ )
    t_spike = Time::ms_stamp(t);
  else 
  {
    // In this case, we need to force the spike time to the grid

    // First, convert the spike time to tics, may not be on grid
    t_spike = Time::ms(t);
    if ( not t_spike.is_grid_time() )
    {
      if ( allow_offgrid_spikes_ )
      {
	// In this case, we need to round to the end of the step
	// in which t lies, ms_stamp does that for us.
	t_spike = Time::ms_stamp(t);
      }
      else
      {
	std::stringstream msg;
	msg << "spike_generator: Time point " << t 
	    << " is not representable in current resolution.";
	throw BadProperty(msg.str());
      }
    }

    assert(t_spike.is_grid_time());

    if ( origin + t_spike == now && shift_now_spikes_ )
      t_spike.advance();
  }
  // t_spike is now the correct time stamp given the chosen options

  // when we get here, we know that the spike time is valid
  spike_stamps_.push_back(t_spike);
  if ( precise_times_ )
  {
    assert(t_spike.get_ms() - t >= 0);
    spike_offsets_.push_back(t_spike.get_ms() - t);
  }
}
예제 #2
0
nest::FixedOutDegreeBuilder::FixedOutDegreeBuilder(
  const GIDCollection& sources,
  const GIDCollection& targets,
  const DictionaryDatum& conn_spec,
  const DictionaryDatum& syn_spec )
  : ConnBuilder( sources, targets, conn_spec, syn_spec )
  , outdegree_( ( *conn_spec )[ Name( "outdegree" ) ] )
{
  // check for potential errors

  // verify that outdegree is not larger than target population if multapses are
  // disabled
  if ( not multapses_ )
  {
    if ( ( outdegree_ > static_cast< long >( targets_->size() ) ) )
      throw BadProperty( "Outdegree cannot be larger than population size." );
  }
}
예제 #3
0
void
Node::set_status_base( const DictionaryDatum& dict )
{
  assert( dict.valid() );
  try
  {
    set_status( dict );
  }
  catch ( BadProperty& e )
  {
    throw BadProperty( String::compose(
      "Setting status of a '%1' with GID %2: %3", get_name(), get_gid(), e.message() ) );
  }

  updateValue< bool >( dict, names::frozen, frozen_ );

  updateValue< bool >( dict, names::needs_prelim_update, needs_prelim_up_ );
}
// finds threshpassing
inline nest::double_t
nest::iaf_psc_alpha_canon::thresh_find_( double_t const dt ) const
{
  switch ( P_.Interpol_ )
  {
  case NO_INTERPOL:
    return dt;
  case LINEAR:
    return thresh_find1_( dt );
  case QUADRATIC:
    return thresh_find2_( dt );
  case CUBIC:
    return thresh_find3_( dt );
  default:
    throw BadProperty( "Invalid interpolation order in iaf_psc_alpha_canon." );
  }
  return 0;
}
예제 #5
0
void
ConnectionManager::set_prototype_status( synindex syn_id, const DictionaryDatum& d )
{
  assert_valid_syn_id( syn_id );
  for ( thread t = 0; t < net_.get_num_threads(); ++t )
  {
    try
    {
      prototypes_[ t ][ syn_id ]->set_status( d );
    }
    catch ( BadProperty& e )
    {
      throw BadProperty( String::compose( "Setting status of prototype '%1': %2",
        prototypes_[ t ][ syn_id ]->get_name(),
        e.message() ) );
    }
  }
}
예제 #6
0
double nest::iaf_psc_exp_ps::Parameters_::set(const DictionaryDatum & d)
{
  // if E_L_ is changed, we need to adjust all variables defined relative to E_L_
  const double ELold = E_L_;
  updateValue<double>(d, names::E_L, E_L_);
  const double delta_EL = E_L_ - ELold;

  updateValue<double>(d, names::tau_m, tau_m_);
  updateValue<double>(d, names::tau_syn_ex, tau_ex_);
  updateValue<double>(d, names::tau_syn_in, tau_in_);
  updateValue<double>(d, names::C_m, c_m_);
  updateValue<double>(d, names::t_ref, t_ref_);
  updateValue<double>(d, names::I_e, I_e_);
  
  if ( updateValue<double>(d, names::V_th, U_th_) )
    U_th_ -= E_L_;
  else
    U_th_ -= delta_EL;
  
  if ( updateValue<double>(d, names::V_min, U_min_) )
    U_min_ -= E_L_;
  else
    U_min_ -= delta_EL;
  
  if ( updateValue<double>(d, names::V_reset, U_reset_) ) 
    U_reset_ -= E_L_;
  else
    U_reset_ -= delta_EL;
  
  if ( U_reset_ >= U_th_ )
    throw BadProperty("Reset potential must be smaller than threshold.");
  
  if ( U_reset_ < U_min_ )
    throw BadProperty("Reset potential must be greater equal minimum potential.");
  
  if ( c_m_ <= 0 )
    throw BadProperty("Capacitance must be strictly positive.");

  if ( Time(Time::ms(t_ref_)).get_steps() < 1 )
    throw BadProperty("Refractory time must be at least one time step.");

  if ( tau_m_ <= 0 || tau_ex_ <= 0 || tau_in_ <= 0 )
    throw BadProperty("All time constants must be strictly positive.");

  if ( tau_m_ == tau_ex_ || tau_m_ == tau_in_ )
    throw BadProperty("Membrane and synapse time constant(s) must differ."
		      "See note in documentation.");

  return delta_EL;
}
static AbstractMask*
create_doughnut( const DictionaryDatum& d )
{
  // The doughnut (actually an annulus) is created using a DifferenceMask
  Position< 2 > center( 0, 0 );
  if ( d->known( names::anchor ) )
    center = getValue< std::vector< double > >( d, names::anchor );

  const double outer = getValue< double >( d, names::outer_radius );
  const double inner = getValue< double >( d, names::inner_radius );
  if ( inner >= outer )
    throw BadProperty(
      "topology::create_doughnut: "
      "inner_radius < outer_radius required." );

  BallMask< 2 > outer_circle( center, outer );
  BallMask< 2 > inner_circle( center, inner );

  return new DifferenceMask< 2 >( outer_circle, inner_circle );
}
예제 #8
0
void
nest::pp_pop_psc_delta::Parameters_::set( const DictionaryDatum& d )
{

  updateValue< long >( d, names::N, N_ );
  updateValue< double >( d, names::rho_0, rho_0_ );
  updateValue< double >( d, names::delta_u, delta_u_ );
  updateValue< double >( d, names::len_kernel, len_kernel_ );

  updateValue< double >( d, names::I_e, I_e_ );
  updateValue< double >( d, names::C_m, c_m_ );
  updateValue< double >( d, names::tau_m, tau_m_ );
  updateValue< std::vector< double > >( d, names::taus_eta, taus_eta_ );
  updateValue< std::vector< double > >( d, names::vals_eta, vals_eta_ );


  if ( taus_eta_.size() != vals_eta_.size() )
    throw BadProperty( String::compose(
      "'taus_eta' and 'vals_eta' need to have the same dimension.\nSize of taus_eta: %1\nSize of "
      "vals_eta: %2",
      taus_eta_.size(),
      vals_eta_.size() ) );

  if ( c_m_ <= 0 )
    throw BadProperty( "Capacitance must be strictly positive." );

  if ( tau_m_ <= 0 )
    throw BadProperty( "The time constants must be strictly positive." );

  for ( uint_t i = 0; i < taus_eta_.size(); i++ )
  {
    if ( taus_eta_[ i ] <= 0 )
      throw BadProperty( "All time constants must be strictly positive." );
  }

  if ( N_ <= 0 )
    throw BadProperty( "Number of neurons must be positive." );

  if ( rho_0_ < 0 )
    throw BadProperty( "Rho_0 cannot be negative." );

  if ( delta_u_ <= 0 )
    throw BadProperty( "Delta_u must be positive." );
}
// finds threshpassing
inline nest::double_t
nest::iaf_psc_alpha_presc::thresh_find_( double_t const dt ) const
{
  switch ( P_.Interpol_ )
  {
  case NO_INTERPOL:
    return dt;
  case LINEAR:
    return thresh_find1_( dt );
  case QUADRATIC:
    return thresh_find2_( dt );
  case CUBIC:
    return thresh_find3_( dt );
  default:
    LOG( M_ERROR,
      "iaf_psc_alpha_presc::thresh_find_()",
      "Invalid interpolation---Internal model error." );
    throw BadProperty();
  }
  return 0;
}
예제 #10
0
  void STDPDopaCommonProperties::set_status(const DictionaryDatum & d, ConnectorModel &cm)
  {
    CommonSynapseProperties::set_status(d, cm);

    long_t vtgid;
    if ( updateValue<long_t>(d, "vt", vtgid) )
    {
      vt_ = dynamic_cast<volume_transmitter *>(NestModule::get_network().get_node(vtgid));

      if(vt_==0)
	throw BadProperty("Dopamine source must be volume transmitter");
    }

    updateValue<double_t>(d, "A_plus", A_plus_);
    updateValue<double_t>(d, "A_minus", A_minus_);
    updateValue<double_t>(d, "tau_plus", tau_plus_);
    updateValue<double_t>(d, "tau_c", tau_c_);
    updateValue<double_t>(d, "tau_n", tau_n_);
    updateValue<double_t>(d, "b", b_);
    updateValue<double_t>(d, "Wmin", Wmin_);
    updateValue<double_t>(d, "Wmax", Wmax_);
  }
void
nest::aeif_cond_alpha::Parameters_::set( const DictionaryDatum& d )
{
  updateValue< double >( d, names::V_th, V_th );
  updateValue< double >( d, names::V_peak, V_peak_ );
  updateValue< double >( d, names::t_ref, t_ref_ );
  updateValue< double >( d, names::E_L, E_L );
  updateValue< double >( d, names::V_reset, V_reset_ );
  updateValue< double >( d, names::E_ex, E_ex );
  updateValue< double >( d, names::E_in, E_in );

  updateValue< double >( d, names::C_m, C_m );
  updateValue< double >( d, names::g_L, g_L );

  updateValue< double >( d, names::tau_syn_ex, tau_syn_ex );
  updateValue< double >( d, names::tau_syn_in, tau_syn_in );

  updateValue< double >( d, names::a, a );
  updateValue< double >( d, names::b, b );
  updateValue< double >( d, names::Delta_T, Delta_T );
  updateValue< double >( d, names::tau_w, tau_w );

  updateValue< double >( d, names::I_e, I_e );

  updateValue< double >( d, names::gsl_error_tol, gsl_error_tol );

  if ( V_peak_ <= V_th )
    throw BadProperty( "V_peak must be larger than threshold." );

  if ( V_reset_ >= V_peak_ )
    throw BadProperty( "Ensure that: V_reset < V_peak ." );

  if ( C_m <= 0 )
  {
    throw BadProperty( "Capacitance must be strictly positive." );
  }

  if ( t_ref_ < 0 )
    throw BadProperty( "Refractory time cannot be negative." );

  if ( tau_syn_ex <= 0 || tau_syn_in <= 0 || tau_w <= 0 )
    throw BadProperty( "All time constants must be strictly positive." );

  if ( gsl_error_tol <= 0. )
    throw BadProperty( "The gsl_error_tol must be strictly positive." );
}
예제 #12
0
void
ConnectionManager::set_synapse_status( index gid,
  synindex syn_id,
  port p,
  thread tid,
  const DictionaryDatum& dict )
{
  assert_valid_syn_id( syn_id );
  try
  {
    validate_pointer( connections_[ tid ].get( gid ) )
      ->set_synapse_status( syn_id, *( prototypes_[ tid ][ syn_id ] ), dict, p );
  }
  catch ( BadProperty& e )
  {
    throw BadProperty(
      String::compose( "Setting status of '%1' connecting from GID %2 to port %3: %4",
        prototypes_[ tid ][ syn_id ]->get_name(),
        gid,
        p,
        e.message() ) );
  }
}
예제 #13
0
void nest::ac_poisson_generator::calibrate()
{
  // This check is placed here so that it only gets executed on actual
  // instances, not on Model instances during a ResetKernel.
  // TODO: Make work for parallel simulation!
  if ( network()->get_num_threads() > 1 || network()->get_num_processes() > 1 )
    throw BadProperty("ac_poisson_generator is presently not suitable"
                      " for parallel simulation.");

  device_.calibrate();

  // time resolution
  const double h = Time::get_resolution().get_ms(); 

  V_.sins_.resize(B_.N_osc_);
  V_.coss_.resize(B_.N_osc_);
  V_.sins_ = std::sin(h * P_.om_);         // block elements
  V_.coss_ = std::cos(h * P_.om_);

  B_.rates_.calibrate();

  return;
}
예제 #14
0
bool nest::ac_poisson_generator::
Parameters_::extract_array_(const DictionaryDatum &d, 
			    const std::string& dname,
			    std::valarray<double>& data) const
{
  if ( d->known(dname) )
  {
    ArrayDatum *ad = dynamic_cast<ArrayDatum *>((*d)[dname].datum());
    if ( ad == 0 )
      throw BadProperty();

    const size_t nd = ad->size();
    data.resize(nd);
    for ( size_t n = 0 ; n < nd ; ++n )
      {
	data[n] = getValue<double>((*ad)[n]);
      }

    return true;
  }
  else
    return false;
}
   /**
   * Set properties of this connection from position p in the properties
   * array given in dictionary.
   */
  void STDP_Tsodyk_Connection_Plus::set_status(const DictionaryDatum & d, index p, ConnectorModel &cm)
  {
    ConnectionHetWD::set_status(d, p, cm);
	
	set_property<double_t>(d, "Us", p, U_);
    set_property<double_t>(d, "tau_pscs", p, tau_psc_);
    set_property<double_t>(d, "tau_facs", p, tau_fac_);
    set_property<double_t>(d, "tau_recs", p, tau_rec_);


    double_t x = x_;
    double_t y = y_;
    set_property<double_t>(d, "xs", p, x);
    set_property<double_t>(d, "ys", p, y);

    if (x + y > 1.0)
    {
      cm.network().message(SLIInterpreter::M_ERROR, 
			   "TsodyksConnection::set_status()", 
			   "x + y must be <= 1.0.");
      throw BadProperty();
    }
    else
    {
      x_ = x;
      y_ = y;
    }

    set_property<double_t>(d, "us", p, u_);

    set_property<double_t>(d, "tau_pluss", p, tau_plus_);
    set_property<double_t>(d, "lambdas", p, lambda_);
    set_property<double_t>(d, "alphas", p, alpha_);
    set_property<double_t>(d, "mu_pluss", p, mu_plus_);
    set_property<double_t>(d, "mu_minuss", p, mu_minus_);
    set_property<double_t>(d, "Wmaxs", p, Wmax_);
  }
예제 #16
0
void nest::iaf_psc_exp_canon::Parameters_::set(const DictionaryDatum& d)
{
  updateValue<double>(d, names::tau_m, tau_m_);
  updateValue<double>(d, names::tau_syn_ex, tau_ex_);
  updateValue<double>(d, names::tau_syn_in, tau_in_);
  updateValue<double>(d, names::C_m, c_m_);
  updateValue<double>(d, names::t_ref, t_ref_);
  updateValue<double>(d, names::E_L, E_L_);
  updateValue<double>(d, names::I_e, I_e_);

  if ( updateValue<double>(d, names::V_th, U_th_) ) 
    U_th_ -= E_L_;

  if ( updateValue<double>(d, names::V_min, U_min_) ) 
    U_min_ -= E_L_;

  if ( updateValue<double>(d, names::V_reset, U_reset_) ) 
    U_reset_ -= E_L_;
  
  long_t tmp;
  if ( updateValue<long_t>(d, names::Interpol_Order, tmp) )
    if ( NO_INTERPOL <= tmp && tmp < END_INTERP_ORDER )
      Interpol_ = static_cast<interpOrder>(tmp);
    else
      throw BadProperty("Interpolation order must be 0, 1, 2 or 3.");

  if ( tau_m_ <= 0 || tau_ex_ <= 0 ||  tau_in_ <= 0 )
    throw BadProperty("All time constants must be strictly positive.");
    
  if ( c_m_ <= 0 )
    throw BadProperty("Capacitance must be strictly positive.");
    
  if ( t_ref_ < 0 )
    throw BadProperty("Refractory time must not be negative.");

  if ( U_reset_ >= U_th_ )
    throw BadProperty("Reset potential must be smaller than threshold.");

  if ( U_reset_ < U_min_ )
    throw BadProperty("Reset potential must be greater equal minimum potential.");
}
예제 #17
0
void
nest::cg_connect( nest::ConnectionGeneratorDatum& cg,
  const index source_id,
  const index target_id,
  const DictionaryDatum& params_map,
  const Name& synmodel_name )
{
  Subnet* sources =
    dynamic_cast< Subnet* >( kernel().node_manager.get_node( source_id ) );
  if ( sources == NULL )
  {
    LOG( M_ERROR, "CGConnect_cg_i_i_D_l", "sources must be a subnet." );
    throw SubnetExpected();
  }
  if ( !sources->is_homogeneous() )
  {
    LOG( M_ERROR,
      "CGConnect_cg_i_i_D_l",
      "sources must be a homogeneous subnet." );
    throw BadProperty();
  }
  if ( dynamic_cast< Subnet* >( *sources->local_begin() ) )
  {
    LOG( M_ERROR,
      "CGConnect_cg_i_i_D_l",
      "Only 1-dim subnets are supported as sources." );
    throw BadProperty();
  }

  Subnet* targets =
    dynamic_cast< Subnet* >( kernel().node_manager.get_node( target_id ) );
  if ( targets == NULL )
  {
    LOG( M_ERROR, "CGConnect_cg_i_i_D_l", "targets must be a subnet." );
    throw SubnetExpected();
  }
  if ( !targets->is_homogeneous() )
  {
    LOG( M_ERROR,
      "CGConnect_cg_i_i_D_l",
      "targets must be a homogeneous subnet." );
    throw BadProperty();
  }
  if ( dynamic_cast< Subnet* >( *targets->local_begin() ) )
  {
    LOG( M_ERROR,
      "CGConnect_cg_i_i_D_l",
      "Only 1-dim subnets are supported as targets." );
    throw BadProperty();
  }

  const Token synmodel =
    kernel().model_manager.get_synapsedict()->lookup( synmodel_name );
  if ( synmodel.empty() )
    throw UnknownSynapseType( synmodel_name.toString() );
  const index synmodel_id = static_cast< index >( synmodel );

  const modelrange source_range =
    kernel().modelrange_manager.get_contiguous_gid_range(
      ( *sources->local_begin() )->get_gid() );
  index source_offset = source_range.get_first_gid();
  RangeSet source_ranges;
  source_ranges.push_back(
    Range( source_range.get_first_gid(), source_range.get_last_gid() ) );

  const modelrange target_range =
    kernel().modelrange_manager.get_contiguous_gid_range(
      ( *targets->local_begin() )->get_gid() );
  index target_offset = target_range.get_first_gid();
  RangeSet target_ranges;
  target_ranges.push_back(
    Range( target_range.get_first_gid(), target_range.get_last_gid() ) );

  cg_connect( cg,
    source_ranges,
    source_offset,
    target_ranges,
    target_offset,
    params_map,
    synmodel_id );
}
예제 #18
0
void
nest::amat2_psc_exp::calibrate()
{
  // ensures initialization in case mm connected after Simulate
  B_.logger_.init();

  const double h = Time::get_resolution().get_ms();

  // numbering of state variables:
  // membrane potential: i_0 = 0, i_syn_ = 1, V_m_ = 2
  // adaptive threshold: V_th_1_ = 1, V_th_2_ = 2

  // --------------------
  // membrane potential
  // --------------------

  const double c = P_.C_;
  const double beta = P_.beta_;

  const double taum = P_.Tau_;
  const double tauE = P_.tau_ex_;
  const double tauI = P_.tau_in_;
  const double tauV = P_.tau_v_;


  // these P are independent
  const double eE = std::exp( -h / P_.tau_ex_ );
  const double eI = std::exp( -h / P_.tau_in_ );
  const double em = std::exp( -h / P_.Tau_ );
  const double e1 = std::exp( -h / P_.tau_1_ );
  const double e2 = std::exp( -h / P_.tau_2_ );
  const double eV = std::exp( -h / P_.tau_v_ );

  // V_.P00 = 1;
  V_.P11_ = eE;
  V_.P22_ = eI;
  V_.P33_ = em;
  V_.P44_ = e1;
  V_.P55_ = e2;
  V_.P66_ = eV;
  V_.P77_ = eV;

  // these depend on the above. Please do not change the order.

  // TODO Shortcut for beta=0

  V_.P30_ = ( taum - em * taum ) / c;
  V_.P31_ = ( ( eE - em ) * tauE * taum ) / ( c * ( tauE - taum ) );
  V_.P32_ = ( ( eI - em ) * tauI * taum ) / ( c * ( tauI - taum ) );

  V_.P60_ = ( beta * ( em - eV ) * taum * tauV ) / ( c * ( taum - tauV ) );
  V_.P61_ =
    ( beta * tauE * taum * tauV * ( eV * ( -tauE + taum ) + em * ( tauE - tauV )
                                    + eE * ( -taum + tauV ) ) )
    / ( c * ( tauE - taum ) * ( tauE - tauV ) * ( taum - tauV ) );
  V_.P62_ =
    ( beta * tauI * taum * tauV * ( eV * ( -tauI + taum ) + em * ( tauI - tauV )
                                    + eI * ( -taum + tauV ) ) )
    / ( c * ( tauI - taum ) * ( tauI - tauV ) * ( taum - tauV ) );
  V_.P63_ = ( beta * ( -em + eV ) * tauV ) / ( taum - tauV );

  V_.P70_ =
    ( beta * taum * tauV
      * ( em * taum * tauV - eV * ( h * ( taum - tauV ) + taum * tauV ) ) )
    / ( c * std::pow( taum - tauV, 2 ) );
  V_.P71_ =
    ( beta * tauE * taum * tauV
      * ( ( em * taum * std::pow( tauE - tauV, 2 )
            - eE * tauE * std::pow( taum - tauV, 2 ) ) * tauV
          - eV * ( tauE - taum )
            * ( h * ( tauE - tauV ) * ( taum - tauV ) + tauE * taum * tauV
                - std::pow( tauV, 3 ) ) ) )
    / ( c * ( tauE - taum ) * std::pow( tauE - tauV, 2 )
        * std::pow( taum - tauV, 2 ) );
  V_.P72_ =
    ( beta * tauI * taum * tauV
      * ( ( em * taum * std::pow( tauI - tauV, 2 )
            - eI * tauI * std::pow( taum - tauV, 2 ) ) * tauV
          - eV * ( tauI - taum )
            * ( h * ( tauI - tauV ) * ( taum - tauV ) + tauI * taum * tauV
                - std::pow( tauV, 3 ) ) ) )
    / ( c * ( tauI - taum ) * std::pow( tauI - tauV, 2 )
        * std::pow( taum - tauV, 2 ) );
  V_.P73_ = ( beta * tauV * ( -( em * taum * tauV )
                              + eV * ( h * ( taum - tauV ) + taum * tauV ) ) )
    / std::pow( taum - tauV, 2 );
  V_.P76_ = eV * h;


  // tau_ref_ specifies the length of the total refractory period as
  // a double in ms. The grid based amat2_psc_exp can only handle refractory
  // periods that are integer multiples of the computation step size (h).
  // To ensure consistency with the overall simulation scheme such conversion
  // should be carried out via objects of class nest::Time. The conversion
  // requires 2 steps:
  //     1. A time object r is constructed defining representation of
  //        tau_ref_ in tics. This representation is then converted to
  //        computation time
  //        steps again by a strategy defined by class nest::Time.
  //     2. The refractory time in units of steps is read out get_steps(), a
  //        member function of class nest::Time.
  //
  // Choosing a tau_ref_ that is not an integer multiple of the computation time
  // step h will led to accurate (up to the resolution h) and self-consistent
  // results. However, a neuron model capable of operating with real valued
  // spike time may exhibit a different effective refractory time.

  V_.RefractoryCountsTot_ = Time( Time::ms( P_.tau_ref_ ) ).get_steps();

  if ( V_.RefractoryCountsTot_ < 1 )
  {
    throw BadProperty(
      "Total refractory time must be at least one time step." );
  }
}
void
nest::RNGManager::set_status( const DictionaryDatum& d )
{
  // have those two for later asking, whether threads have changed:
  long n_threads;
  bool n_threads_updated =
    updateValue< long >( d, "local_num_threads", n_threads );

  // set RNGs --- MUST come after n_threads_ is updated
  if ( d->known( "rngs" ) )
  {
    // this array contains pre-seeded RNGs, so they can be used
    // directly, no seeding required
    ArrayDatum* ad = dynamic_cast< ArrayDatum* >( ( *d )[ "rngs" ].datum() );
    if ( ad == 0 )
      throw BadProperty();

    // n_threads_ is the new value after a change of the number of
    // threads
    if ( ad->size()
      != ( size_t )( kernel().vp_manager.get_num_virtual_processes() ) )
    {
      LOG( M_ERROR,
        "RNGManager::set_status",
        "Number of RNGs must equal number of virtual processes "
        "(threads*processes). RNGs "
        "unchanged." );
      throw DimensionMismatch(
        ( size_t )( kernel().vp_manager.get_num_virtual_processes() ),
        ad->size() );
    }

    // delete old generators, insert new generators this code is
    // robust under change of thread number in this call to
    // set_status, as long as it comes AFTER n_threads_ has been
    // upated
    rng_.clear();
    for ( index i = 0; i < ad->size(); ++i )
      if ( kernel().vp_manager.is_local_vp( i ) )
        rng_.push_back( getValue< librandom::RngDatum >(
          ( *ad )[ kernel().vp_manager.suggest_vp( i ) ] ) );
  }
  else if ( n_threads_updated && kernel().node_manager.size() == 0 )
  {
    LOG( M_WARNING,
      "RNGManager::set_status",
      "Equipping threads with new default RNGs" );
    create_rngs_();
  }

  if ( d->known( "rng_seeds" ) )
  {
    ArrayDatum* ad =
      dynamic_cast< ArrayDatum* >( ( *d )[ "rng_seeds" ].datum() );
    if ( ad == 0 )
      throw BadProperty();

    if ( ad->size()
      != ( size_t )( kernel().vp_manager.get_num_virtual_processes() ) )
    {
      LOG( M_ERROR,
        "RNGManager::set_status",
        "Number of seeds must equal number of virtual processes "
        "(threads*processes). RNGs unchanged." );
      throw DimensionMismatch(
        ( size_t )( kernel().vp_manager.get_num_virtual_processes() ),
        ad->size() );
    }

    // check if seeds are unique
    std::set< ulong_t > seedset;
    for ( index i = 0; i < ad->size(); ++i )
    {
      long s = ( *ad )[ i ]; // SLI has no ulong tokens
      if ( !seedset.insert( s ).second )
      {
        LOG( M_WARNING,
          "RNGManager::set_status",
          "Seeds are not unique across threads!" );
        break;
      }
    }

    // now apply seeds, resets generators automatically
    for ( index i = 0; i < ad->size(); ++i )
    {
      long s = ( *ad )[ i ];

      if ( kernel().vp_manager.is_local_vp( i ) )
        rng_[ kernel().vp_manager.vp_to_thread(
                kernel().vp_manager.suggest_vp( i ) ) ]->seed( s );

      rng_seeds_[ i ] = s;
    }
  } // if rng_seeds

  // set GRNG
  if ( d->known( "grng" ) )
  {
    // pre-seeded grng that can be used directly, no seeding required
    updateValue< librandom::RngDatum >( d, "grng", grng_ );
  }
  else if ( n_threads_updated && kernel().node_manager.size() == 0 )
  {
    LOG( M_WARNING,
      "RNGManager::set_status",
      "Equipping threads with new default GRNG" );
    create_grng_();
  }

  if ( d->known( "grng_seed" ) )
  {
    const long gseed = getValue< long >( d, "grng_seed" );

    // check if grng seed is unique with respect to rng seeds
    // if grng_seed and rng_seeds given in one SetStatus call
    std::set< ulong_t > seedset;
    seedset.insert( gseed );
    if ( d->known( "rng_seeds" ) )
    {
      ArrayDatum* ad_rngseeds =
        dynamic_cast< ArrayDatum* >( ( *d )[ "rng_seeds" ].datum() );
      if ( ad_rngseeds == 0 )
        throw BadProperty();
      for ( index i = 0; i < ad_rngseeds->size(); ++i )
      {
        const long vpseed = ( *ad_rngseeds )[ i ]; // SLI has no ulong tokens
        if ( !seedset.insert( vpseed ).second )
        {
          LOG( M_WARNING,
            "RNGManager::set_status",
            "Seeds are not unique across threads!" );
          break;
        }
      }
    }
    // now apply seed, resets generator automatically
    grng_seed_ = gseed;
    grng_->seed( gseed );

  } // if grng_seed
}
예제 #20
0
nest::ConnBuilder::ConnBuilder( const GIDCollection& sources,
  const GIDCollection& targets,
  const DictionaryDatum& conn_spec,
  const DictionaryDatum& syn_spec )
  : sources_( &sources )
  , targets_( &targets )
  , autapses_( true )
  , multapses_( true )
  , symmetric_( false )
  , exceptions_raised_( kernel().vp_manager.get_num_threads() )
  , synapse_model_( kernel().model_manager.get_synapsedict()->lookup(
      "static_synapse" ) )
  , weight_( 0 )
  , delay_( 0 )
  , param_dicts_()
  , parameters_requiring_skipping_()
{
  // read out rule-related parameters -------------------------
  //  - /rule has been taken care of above
  //  - rule-specific params are handled by subclass c'tor
  updateValue< bool >( conn_spec, names::autapses, autapses_ );
  updateValue< bool >( conn_spec, names::multapses, multapses_ );
  updateValue< bool >( conn_spec, names::symmetric, symmetric_ );

  // read out synapse-related parameters ----------------------
  if ( !syn_spec->known( names::model ) )
    throw BadProperty( "Synapse spec must contain synapse model." );
  const std::string syn_name = ( *syn_spec )[ names::model ];
  if ( not kernel().model_manager.get_synapsedict()->known( syn_name ) )
    throw UnknownSynapseType( syn_name );

  // if another synapse than static_synapse is defined we need to make
  // sure that Connect can process all parameter specified
  if ( syn_name != "static_synapse" )
    check_synapse_params_( syn_name, syn_spec );

  synapse_model_ = kernel().model_manager.get_synapsedict()->lookup( syn_name );

  DictionaryDatum syn_defaults =
    kernel().model_manager.get_connector_defaults( synapse_model_ );

  // All synapse models have the possibility to set the delay (see
  // SynIdDelay), but some have homogeneous weights, hence it should
  // be possible to set the delay without the weight.
  default_weight_ = !syn_spec->known( names::weight );

  default_delay_ = !syn_spec->known( names::delay );

  // If neither weight nor delay are given in the dict, we handle this
  // separately. Important for hom_w synapses, on which weight cannot
  // be set. However, we use default weight and delay for _all_ types
  // of synapses.
  default_weight_and_delay_ = ( default_weight_ && default_delay_ );

#ifdef HAVE_MUSIC
  // We allow music_channel as alias for receptor_type during
  // connection setup
  ( *syn_defaults )[ names::music_channel ] = 0;
#endif

  if ( !default_weight_and_delay_ )
  {
    weight_ = syn_spec->known( names::weight )
      ? ConnParameter::create( ( *syn_spec )[ names::weight ],
          kernel().vp_manager.get_num_threads() )
      : ConnParameter::create( ( *syn_defaults )[ names::weight ],
          kernel().vp_manager.get_num_threads() );
    register_parameters_requiring_skipping_( *weight_ );
    delay_ = syn_spec->known( names::delay )
      ? ConnParameter::create(
          ( *syn_spec )[ names::delay ], kernel().vp_manager.get_num_threads() )
      : ConnParameter::create( ( *syn_defaults )[ names::delay ],
          kernel().vp_manager.get_num_threads() );
  }
  else if ( default_weight_ )
  {
    delay_ = syn_spec->known( names::delay )
      ? ConnParameter::create(
          ( *syn_spec )[ names::delay ], kernel().vp_manager.get_num_threads() )
      : ConnParameter::create( ( *syn_defaults )[ names::delay ],
          kernel().vp_manager.get_num_threads() );
  }
  register_parameters_requiring_skipping_( *delay_ );
  // Structural plasticity parameters
  // Check if both pre and post synaptic element are provided
  if ( syn_spec->known( names::pre_synaptic_element )
    && syn_spec->known( names::post_synaptic_element ) )
  {
    pre_synaptic_element_name =
      getValue< std::string >( syn_spec, names::pre_synaptic_element );
    post_synaptic_element_name =
      getValue< std::string >( syn_spec, names::post_synaptic_element );
  }
  else
  {
    if ( syn_spec->known( names::pre_synaptic_element )
      || syn_spec->known( names::post_synaptic_element ) )
    {
      throw BadProperty(
        "In order to use structural plasticity, both a pre and post synaptic "
        "element must be specified" );
    }
    pre_synaptic_element_name = "";
    post_synaptic_element_name = "";
  }

  // synapse-specific parameters
  // TODO: Can we create this set once and for all?
  //       Should not be done as static initialization, since
  //       that might conflict with static initialization of
  //       Name system.
  std::set< Name > skip_set;
  skip_set.insert( names::weight );
  skip_set.insert( names::delay );
  skip_set.insert( Name( "min_delay" ) );
  skip_set.insert( Name( "max_delay" ) );
  skip_set.insert( Name( "num_connections" ) );
  skip_set.insert( Name( "num_connectors" ) );
  skip_set.insert( Name( "property_object" ) );
  skip_set.insert( Name( "synapsemodel" ) );

  for ( Dictionary::const_iterator default_it = syn_defaults->begin();
        default_it != syn_defaults->end();
        ++default_it )
  {
    const Name param_name = default_it->first;
    if ( skip_set.find( param_name ) != skip_set.end() )
      continue; // weight, delay or not-settable parameter

    if ( syn_spec->known( param_name ) )
    {
      synapse_params_[ param_name ] = ConnParameter::create(
        ( *syn_spec )[ param_name ], kernel().vp_manager.get_num_threads() );
      register_parameters_requiring_skipping_( *synapse_params_[ param_name ] );
    }
  }

  // Now create dictionary with dummy values that we will use
  // to pass settings to the synapses created. We create it here
  // once to avoid re-creating the object over and over again.
  if ( synapse_params_.size() > 0 )
  {
    for ( index t = 0; t < kernel().vp_manager.get_num_threads(); ++t )
    {
      param_dicts_.push_back( new Dictionary() );

      for ( ConnParameterMap::const_iterator it = synapse_params_.begin();
            it != synapse_params_.end();
            ++it )
      {
        if ( it->first == names::receptor_type
          || it->first == names::music_channel
          || it->first == names::synapse_label )
          ( *param_dicts_[ t ] )[ it->first ] = Token( new IntegerDatum( 0 ) );
        else
          ( *param_dicts_[ t ] )[ it->first ] = Token( new DoubleDatum( 0.0 ) );
      }
    }
  }

  // If symmetric_ is requested call reset on all parameters in order
  // to check if all parameters support symmetric connections
  if ( symmetric_ )
  {
    if ( weight_ )
    {
      weight_->reset();
    }
    if ( delay_ )
    {
      delay_->reset();
    }
    for ( ConnParameterMap::const_iterator it = synapse_params_.begin();
          it != synapse_params_.end();
          ++it )
    {
      it->second->reset();
    }
  }
}
double
iaf_psc_alpha_multisynapse::Parameters_::set( const DictionaryDatum& d )
{
  // if E_L_ is changed, we need to adjust all variables defined relative to
  // E_L_
  const double ELold = E_L_;
  updateValue< double >( d, names::E_L, E_L_ );
  const double delta_EL = E_L_ - ELold;

  if ( updateValue< double >( d, names::V_reset, V_reset_ ) )
  {
    V_reset_ -= E_L_;
  }
  else
  {
    V_reset_ -= delta_EL;
  }
  if ( updateValue< double >( d, names::V_th, Theta_ ) )
  {
    Theta_ -= E_L_;
  }
  else
  {
    Theta_ -= delta_EL;
  }
  if ( updateValue< double >( d, names::V_min, LowerBound_ ) )
  {
    LowerBound_ -= E_L_;
  }
  else
  {
    LowerBound_ -= delta_EL;
  }
  updateValue< double >( d, names::I_e, I_e_ );
  updateValue< double >( d, names::C_m, C_ );
  updateValue< double >( d, names::tau_m, Tau_ );
  updateValue< double >( d, names::t_ref, refractory_time_ );

  if ( C_ <= 0 )
  {
    throw BadProperty( "Capacitance must be strictly positive." );
  }
  if ( Tau_ <= 0. )
  {
    throw BadProperty( "Membrane time constant must be strictly positive." );
  }
  const size_t old_n_receptors = this->n_receptors_();
  if ( updateValue< std::vector< double > >( d, "tau_syn", tau_syn_ ) )
  {
    if ( this->n_receptors_() != old_n_receptors && has_connections_ == true )
    {
      throw BadProperty(
        "The neuron has connections, therefore the number of ports cannot be "
        "reduced." );
    }
    for ( size_t i = 0; i < tau_syn_.size(); ++i )
    {
      if ( tau_syn_[ i ] <= 0 )
      {
        throw BadProperty(
          "All synaptic time constants must be strictly positive." );
      }
    }
  }

  if ( refractory_time_ < 0. )
  {
    throw BadProperty( "Refractory time must not be negative." );
  }

  if ( V_reset_ >= Theta_ )
  {
    throw BadProperty( "Reset potential must be smaller than threshold." );
  }

  return delta_EL;
}
예제 #22
0
inline void
nest::ConnBuilder::check_synapse_params_( std::string syn_name,
  const DictionaryDatum& syn_spec )
{
  // throw error if weight is specified with static_synapse_hom_w
  if ( syn_name == "static_synapse_hom_w" )
  {
    if ( syn_spec->known( names::weight ) )
      throw BadProperty(
        "Weight cannot be specified since it needs to be equal "
        "for all connections when static_synapse_hom_w is used." );
    return;
  }


  // throw error if n or a are set in quantal_stp_synapse, Connect cannot handle
  // them since they are integer
  if ( syn_name == "quantal_stp_synapse" )
  {
    if ( syn_spec->known( names::n ) )
      throw NotImplemented(
        "Connect doesn't support the setting of parameter "
        "n in quantal_stp_synapse. Use SetDefaults() or CopyModel()." );
    if ( syn_spec->known( names::a ) )
      throw NotImplemented(
        "Connect doesn't support the setting of parameter "
        "a in quantal_stp_synapse. Use SetDefaults() or CopyModel()." );
    return;
  }

  // print warning if delay is specified outside cont_delay_synapse
  if ( syn_name == "cont_delay_synapse" )
  {
    if ( syn_spec->known( names::delay ) )
      LOG( M_WARNING,
        "Connect",
        "The delay will be rounded to the next multiple of the time step. "
        "To use a more precise time delay it needs to be defined within "
        "the synapse, e.g. with CopyModel()." );
    return;
  }

  // throw error if no volume transmitter is defined or parameters are specified
  // that need to be introduced via CopyModel or SetDefaults
  if ( syn_name == "stdp_dopamine_synapse" )
  {
    if ( syn_spec->known( "vt" ) )
      throw NotImplemented(
        "Connect doesn't support the direct specification of the "
        "volume transmitter of stdp_dopamine_synapse in syn_spec."
        "Use SetDefaults() or CopyModel()." );
    // setting of parameter c and n not thread save
    if ( kernel().vp_manager.get_num_threads() > 1 )
    {
      if ( syn_spec->known( names::c ) )
        throw NotImplemented(
          "For multi-threading Connect doesn't support the setting "
          "of parameter c in stdp_dopamine_synapse. "
          "Use SetDefaults() or CopyModel()." );
      if ( syn_spec->known( names::n ) )
        throw NotImplemented(
          "For multi-threading Connect doesn't support the setting "
          "of parameter n in stdp_dopamine_synapse. "
          "Use SetDefaults() or CopyModel()." );
    }
    std::string param_arr[] = {
      "A_minus", "A_plus", "Wmax", "Wmin", "b", "tau_c", "tau_n", "tau_plus"
    };
    std::vector< std::string > param_vec( param_arr, param_arr + 8 );
    for ( std::vector< std::string >::iterator it = param_vec.begin();
          it != param_vec.end();
          it++ )
    {
      if ( syn_spec->known( *it ) )
        throw NotImplemented(
          "Connect doesn't support the setting of parameter " + *it
          + " in stdp_dopamine_synapse. Use SetDefaults() or CopyModel()." );
    }
    return;
  }
}
예제 #23
0
inline void
nest::ConnBuilder::single_connect_( index sgid,
  Node& target,
  thread target_thread,
  librandom::RngPtr& rng )
{
  if ( param_dicts_.empty() ) // indicates we have no synapse params
  {
    if ( default_weight_and_delay_ )
      kernel().connection_manager.connect(
        sgid, &target, target_thread, synapse_model_ );
    else if ( default_weight_ )
      kernel().connection_manager.connect( sgid,
        &target,
        target_thread,
        synapse_model_,
        delay_->value_double( target_thread, rng ) );
    else
    {
      double delay = delay_->value_double( target_thread, rng );
      double weight = weight_->value_double( target_thread, rng );
      kernel().connection_manager.connect(
        sgid, &target, target_thread, synapse_model_, delay, weight );
    }
  }
  else
  {
    assert( kernel().vp_manager.get_num_threads() == param_dicts_.size() );

    for ( ConnParameterMap::const_iterator it = synapse_params_.begin();
          it != synapse_params_.end();
          ++it )
    {
      if ( it->first == names::receptor_type
        || it->first == names::music_channel
        || it->first == names::synapse_label )
      {
        try
        {
          // change value of dictionary entry without allocating new datum
          IntegerDatum* id = static_cast< IntegerDatum* >(
            ( ( *param_dicts_[ target_thread ] )[ it->first ] ).datum() );
          ( *id ) = it->second->value_int( target_thread, rng );
        }
        catch ( KernelException& e )
        {
          if ( it->first == names::receptor_type )
          {
            throw BadProperty( "Receptor type must be of type integer." );
          }
          else if ( it->first == names::music_channel )
          {
            throw BadProperty( "Music channel type must be of type integer." );
          }
          else if ( it->first == names::synapse_label )
          {
            throw BadProperty( "Synapse label must be of type integer." );
          }
        }
      }
      else
      {
        // change value of dictionary entry without allocating new datum
        DoubleDatum* dd = static_cast< DoubleDatum* >(
          ( ( *param_dicts_[ target_thread ] )[ it->first ] ).datum() );
        ( *dd ) = it->second->value_double( target_thread, rng );
      }
    }

    if ( default_weight_and_delay_ )
      kernel().connection_manager.connect( sgid,
        &target,
        target_thread,
        synapse_model_,
        param_dicts_[ target_thread ] );
    else if ( default_weight_ )
      kernel().connection_manager.connect( sgid,
        &target,
        target_thread,
        synapse_model_,
        param_dicts_[ target_thread ],
        delay_->value_double( target_thread, rng ) );
    else
    {
      double delay = delay_->value_double( target_thread, rng );
      double weight = weight_->value_double( target_thread, rng );
      kernel().connection_manager.connect( sgid,
        &target,
        target_thread,
        synapse_model_,
        param_dicts_[ target_thread ],
        delay,
        weight );
    }
  }
}
예제 #24
0
MaskDatum
TopologyModule::create_mask( const Token& t )
{
  // t can be either an existing MaskDatum, or a Dictionary containing
  // mask parameters
  MaskDatum* maskd = dynamic_cast< MaskDatum* >( t.datum() );
  if ( maskd )
  {
    return *maskd;
  }
  else
  {

    DictionaryDatum* dd = dynamic_cast< DictionaryDatum* >( t.datum() );
    if ( dd == 0 )
    {
      throw BadProperty( "Mask must be masktype or dictionary." );
    }

    // The dictionary should contain one key which is the name of the
    // mask type, and optionally the key 'anchor'. To find the unknown
    // mask type key, we must loop through all keys. The value for the
    // anchor key will be stored in the anchor_token variable.
    Token anchor_token;
    bool has_anchor = false;
    AbstractMask* mask = 0;

    for ( Dictionary::iterator dit = ( *dd )->begin(); dit != ( *dd )->end();
          ++dit )
    {

      if ( dit->first == names::anchor )
      {

        anchor_token = dit->second;
        has_anchor = true;
      }
      else
      {

        if ( mask != 0 )
        { // mask has already been defined
          throw BadProperty(
            "Mask definition dictionary contains extraneous items." );
        }
        mask =
          create_mask( dit->first, getValue< DictionaryDatum >( dit->second ) );
      }
    }

    if ( has_anchor )
    {

      // The anchor may be an array of doubles (a spatial position), or a
      // dictionary containing the keys 'column' and 'row' (for grid
      // masks only)
      try
      {

        std::vector< double > anchor =
          getValue< std::vector< double > >( anchor_token );
        AbstractMask* amask;

        switch ( anchor.size() )
        {
        case 2:
          amask = new AnchoredMask< 2 >(
            dynamic_cast< Mask< 2 >& >( *mask ), anchor );
          break;
        case 3:
          amask = new AnchoredMask< 3 >(
            dynamic_cast< Mask< 3 >& >( *mask ), anchor );
          break;
        default:
          throw BadProperty( "Anchor must be 2- or 3-dimensional." );
        }

        delete mask;
        mask = amask;
      }
      catch ( TypeMismatch& e )
      {

        DictionaryDatum ad = getValue< DictionaryDatum >( anchor_token );

        int dim = 2;
        int column = getValue< long >( ad, names::column );
        int row = getValue< long >( ad, names::row );
        int layer;
        if ( ad->known( names::layer ) )
        {
          layer = getValue< long >( ad, names::layer );
          dim = 3;
        }
        switch ( dim )
        {
        case 2:
          try
          {
            GridMask< 2 >& grid_mask_2d =
              dynamic_cast< GridMask< 2 >& >( *mask );
            grid_mask_2d.set_anchor( Position< 2, int >( column, row ) );
          }
          catch ( std::bad_cast& e )
          {
            throw BadProperty( "Mask must be 2-dimensional grid mask." );
          }
          break;
        case 3:
          try
          {
            GridMask< 3 >& grid_mask_3d =
              dynamic_cast< GridMask< 3 >& >( *mask );
            grid_mask_3d.set_anchor( Position< 3, int >( column, row, layer ) );
          }
          catch ( std::bad_cast& e )
          {
            throw BadProperty( "Mask must be 3-dimensional grid mask." );
          }
          break;
        }
      }
    }

    return mask;
  }
}
bool
nest::correlospinmatrix_detector::Parameters_::set( const DictionaryDatum& d,
  const correlospinmatrix_detector& n )
{
  bool reset = false;
  double_t t;
  long_t N;

  if ( updateValue< long_t >( d, names::N_channels, N ) )
  {
    if ( N < 1 )
    {
      throw BadProperty( "/N_channels can only be larger than zero." );
    }
    else
    {
      N_channels_ = N;
      reset = true;
    }
  }

  if ( updateValue< double_t >( d, names::delta_tau, t ) )
  {
    delta_tau_ = Time::ms( t );
    reset = true;
    if ( t < 0 )
    {
      throw BadProperty( "/delta_tau must not be negative." );
    }
  }

  if ( updateValue< double_t >( d, names::tau_max, t ) )
  {
    tau_max_ = Time::ms( t );
    reset = true;
    if ( t < 0 )
    {
      throw BadProperty( "/tau_max must not be negative." );
    }
  }

  if ( updateValue< double_t >( d, names::Tstart, t ) )
  {
    Tstart_ = Time::ms( t );
    reset = true;
    if ( t < 0 )
    {
      throw BadProperty( "/Tstart must not be negative." );
    }
  }

  if ( updateValue< double_t >( d, names::Tstop, t ) )
  {
    Tstop_ = Time::ms( t );
    reset = true;
    if ( t < 0 )
    {
      throw BadProperty( "/Tstop must not be negative." );
    }
  }

  if ( !delta_tau_.is_step() )
  {
    throw StepMultipleRequired( n.get_name(), names::delta_tau, delta_tau_ );
  }

  if ( !tau_max_.is_multiple_of( delta_tau_ ) )
  {
    throw TimeMultipleRequired(
      n.get_name(), names::tau_max, tau_max_, names::delta_tau, delta_tau_ );
  }
  return reset;
}
예제 #26
0
double
nest::iaf_psc_alpha_canon::Parameters_::set( const DictionaryDatum& d )
{
  // if E_L_ is changed, we need to adjust all variables defined relative to
  // E_L_
  const double ELold = E_L_;
  updateValue< double >( d, names::E_L, E_L_ );
  const double delta_EL = E_L_ - ELold;

  updateValue< double >( d, names::tau_m, tau_m_ );
  updateValue< double >( d, names::tau_syn, tau_syn_ );
  updateValue< double >( d, names::C_m, c_m_ );
  updateValue< double >( d, names::t_ref, t_ref_ );
  updateValue< double >( d, names::I_e, I_e_ );

  if ( updateValue< double >( d, names::V_th, U_th_ ) )
  {
    U_th_ -= E_L_;
  }
  else
  {
    U_th_ -= delta_EL;
  }

  if ( updateValue< double >( d, names::V_min, U_min_ ) )
  {
    U_min_ -= E_L_;
  }
  else
  {
    U_min_ -= delta_EL;
  }

  if ( updateValue< double >( d, names::V_reset, U_reset_ ) )
  {
    U_reset_ -= E_L_;
  }
  else
  {
    U_reset_ -= delta_EL;
  }

  long tmp;
  if ( updateValue< long >( d, names::Interpol_Order, tmp ) )
  {
    if ( NO_INTERPOL <= tmp && tmp < END_INTERP_ORDER )
    {
      Interpol_ = static_cast< interpOrder >( tmp );
    }
    else
    {
      throw BadProperty(
        "Invalid interpolation order. "
        "Valid orders are 0, 1, 2, 3." );
    }
  }
  if ( U_reset_ >= U_th_ )
  {
    throw BadProperty( "Reset potential must be smaller than threshold." );
  }
  if ( U_reset_ < U_min_ )
  {
    throw BadProperty(
      "Reset potential must be greater equal minimum potential." );
  }
  if ( c_m_ <= 0 )
  {
    throw BadProperty( "Capacitance must be strictly positive." );
  }

  if ( Time( Time::ms( t_ref_ ) ).get_steps() < 1 )
  {
    throw BadProperty( "Refractory time must be at least one time step." );
  }
  if ( tau_m_ <= 0 || tau_syn_ <= 0 )
  {
    throw BadProperty( "All time constants must be strictly positive." );
  }

  return delta_EL;
}
예제 #27
0
index NodeManager::add_node( index mod, long n ) // no_p
{
  assert( current_ != 0 );
  assert( root_ != 0 );

  if ( mod >= kernel().model_manager.get_num_node_models() )
  {
    throw UnknownModelID( mod );
  }

  if ( n < 1 )
  {
    throw BadProperty();
  }

  const thread n_threads = kernel().vp_manager.get_num_threads();
  assert( n_threads > 0 );

  const index min_gid = local_nodes_.get_max_gid() + 1;
  const index max_gid = min_gid + n;

  Model* model = kernel().model_manager.get_model( mod );
  assert( model != 0 );

  model->deprecation_warning( "Create" );

  /* current_ points to the instance of the current subnet on thread 0.
     The following code makes subnet a pointer to the wrapper container
     containing the instances of the current subnet on all threads.
   */
  const index subnet_gid = current_->get_gid();
  Node* subnet_node = local_nodes_.get_node_by_gid( subnet_gid );
  assert( subnet_node != 0 );

  SiblingContainer* subnet_container =
    dynamic_cast< SiblingContainer* >( subnet_node );
  assert( subnet_container != 0 );
  assert( subnet_container->num_thread_siblings()
    == static_cast< size_t >( n_threads ) );
  assert( subnet_container->get_thread_sibling( 0 ) == current_ );

  if ( max_gid > local_nodes_.max_size() || max_gid < min_gid )
  {
    LOG( M_ERROR,
      "NodeManager::add:node",
      "Requested number of nodes will overflow the memory." );
    LOG( M_ERROR, "NodeManager::add:node", "No nodes were created." );
    throw KernelException( "OutOfMemory" );
  }
  kernel().modelrange_manager.add_range( mod, min_gid, max_gid - 1 );

  if ( model->potential_global_receiver()
    and kernel().mpi_manager.get_num_rec_processes() > 0 )
  {
    // In this branch we create nodes for global receivers
    const int n_per_process = n / kernel().mpi_manager.get_num_rec_processes();
    const int n_per_thread = n_per_process / n_threads + 1;

    // We only need to reserve memory on the ranks on which we
    // actually create nodes. In this if-branch ---> Only on recording
    // processes
    if ( kernel().mpi_manager.get_rank()
      >= kernel().mpi_manager.get_num_sim_processes() )
    {
      local_nodes_.reserve( std::ceil( static_cast< double >( max_gid )
        / kernel().mpi_manager.get_num_sim_processes() ) );
      for ( thread t = 0; t < n_threads; ++t )
      {
        // Model::reserve() reserves memory for n ADDITIONAL nodes on thread t
        model->reserve_additional( t, n_per_thread );
      }
    }

    for ( size_t gid = min_gid; gid < max_gid; ++gid )
    {
      const thread vp = kernel().vp_manager.suggest_rec_vp( get_n_gsd() );
      const thread t = kernel().vp_manager.vp_to_thread( vp );

      if ( kernel().vp_manager.is_local_vp( vp ) )
      {
        Node* newnode = model->allocate( t );
        newnode->set_gid_( gid );
        newnode->set_model_id( mod );
        newnode->set_thread( t );
        newnode->set_vp( vp );
        newnode->set_has_proxies( true );
        newnode->set_local_receiver( false );

        local_nodes_.add_local_node( *newnode ); // put into local nodes list

        current_->add_node( newnode ); // and into current subnet, thread 0.
      }
      else
      {
        local_nodes_.add_remote_node( gid ); // ensures max_gid is correct
        current_->add_remote_node( gid, mod );
      }
      increment_n_gsd();
    }
  }

  else if ( model->has_proxies() )
  {
    // In this branch we create nodes for all GIDs which are on a local thread
    const int n_per_process = n / kernel().mpi_manager.get_num_sim_processes();
    const int n_per_thread = n_per_process / n_threads + 1;

    // We only need to reserve memory on the ranks on which we
    // actually create nodes. In this if-branch ---> Only on
    // simulation processes
    if ( kernel().mpi_manager.get_rank()
      < kernel().mpi_manager.get_num_sim_processes() )
    {
      // TODO: This will work reasonably for round-robin. The extra 50 entries
      //       are for subnets and devices.
      local_nodes_.reserve(
        std::ceil( static_cast< double >( max_gid )
          / kernel().mpi_manager.get_num_sim_processes() ) + 50 );
      for ( thread t = 0; t < n_threads; ++t )
      {
        // Model::reserve() reserves memory for n ADDITIONAL nodes on thread t
        // reserves at least one entry on each thread, nobody knows why
        model->reserve_additional( t, n_per_thread );
      }
    }

    size_t gid;
    if ( kernel().vp_manager.is_local_vp(
           kernel().vp_manager.suggest_vp( min_gid ) ) )
    {
      gid = min_gid;
    }
    else
    {
      gid = next_local_gid_( min_gid );
    }
    size_t next_lid = current_->global_size() + gid - min_gid;
    // The next loop will not visit every node, if more than one rank is
    // present.
    // Since we already know what range of gids will be created, we can tell the
    // current subnet the range and subsequent calls to
    // `current_->add_remote_node()`
    // become irrelevant.
    current_->add_gid_range( min_gid, max_gid - 1 );

    // min_gid is first valid gid i should create, hence ask for the first local
    // gid after min_gid-1
    while ( gid < max_gid )
    {
      const thread vp = kernel().vp_manager.suggest_vp( gid );
      const thread t = kernel().vp_manager.vp_to_thread( vp );

      if ( kernel().vp_manager.is_local_vp( vp ) )
      {
        Node* newnode = model->allocate( t );
        newnode->set_gid_( gid );
        newnode->set_model_id( mod );
        newnode->set_thread( t );
        newnode->set_vp( vp );

        local_nodes_.add_local_node( *newnode ); // put into local nodes list
        current_->add_node( newnode ); // and into current subnet, thread 0.

        // lid setting is wrong, if a range is set, as the subnet already
        // assumes,
        // the nodes are available.
        newnode->set_lid_( next_lid );
        const size_t next_gid = next_local_gid_( gid );
        next_lid += next_gid - gid;
        gid = next_gid;
      }
      else
      {
        ++gid; // brutal fix, next_lid has been set in if-branch
      }
    }
    // if last gid is not on this process, we need to add it as a remote node
    if ( not kernel().vp_manager.is_local_vp(
           kernel().vp_manager.suggest_vp( max_gid - 1 ) ) )
    {
      local_nodes_.add_remote_node( max_gid - 1 ); // ensures max_gid is correct
      current_->add_remote_node( max_gid - 1, mod );
    }
  }
  else if ( not model->one_node_per_process() )
  {
    // We allocate space for n containers which will hold the threads
    // sorted. We use SiblingContainers to store the instances for
    // each thread to exploit the very efficient memory allocation for
    // nodes.
    //
    // These containers are registered in the global nodes_ array to
    // provide access to the instances both for manipulation by SLI
    // functions and so that NodeManager::calibrate() can discover the
    // instances and register them for updating.
    //
    // The instances are also registered with the instance of the
    // current subnet for the thread to which the created instance
    // belongs. This is mainly important so that the subnet structure
    // is preserved on all VPs.  Node enumeration is done on by the
    // registration with the per-thread instances.
    //
    // The wrapper container can be addressed under the GID assigned
    // to no-proxy node created. If this no-proxy node is NOT a
    // container (e.g. a device), then each instance can be retrieved
    // by giving the respective thread-id to get_node(). Instances of
    // SiblingContainers cannot be addressed individually.
    //
    // The allocation of the wrapper containers is spread over threads
    // to balance memory load.
    size_t container_per_thread = n / n_threads + 1;

    // since we create the n nodes on each thread, we reserve the full load.
    for ( thread t = 0; t < n_threads; ++t )
    {
      model->reserve_additional( t, n );
      siblingcontainer_model_->reserve_additional( t, container_per_thread );
      static_cast< Subnet* >( subnet_container->get_thread_sibling( t ) )
        ->reserve( n );
    }

    // The following loop creates n nodes. For each node, a wrapper is created
    // and filled with one instance per thread, in total n * n_thread nodes in
    // n wrappers.
    local_nodes_.reserve(
      std::ceil( static_cast< double >( max_gid )
        / kernel().mpi_manager.get_num_sim_processes() ) + 50 );
    for ( index gid = min_gid; gid < max_gid; ++gid )
    {
      thread thread_id = kernel().vp_manager.vp_to_thread(
        kernel().vp_manager.suggest_vp( gid ) );

      // Create wrapper and register with nodes_ array.
      SiblingContainer* container = static_cast< SiblingContainer* >(
        siblingcontainer_model_->allocate( thread_id ) );
      container->set_model_id(
        -1 ); // mark as pseudo-container wrapping replicas, see reset_network()
      container->reserve( n_threads ); // space for one instance per thread
      container->set_gid_( gid );
      local_nodes_.add_local_node( *container );

      // Generate one instance of desired model per thread
      for ( thread t = 0; t < n_threads; ++t )
      {
        Node* newnode = model->allocate( t );
        newnode->set_gid_( gid ); // all instances get the same global id.
        newnode->set_model_id( mod );
        newnode->set_thread( t );
        newnode->set_vp( kernel().vp_manager.thread_to_vp( t ) );

        // Register instance with wrapper
        // container has one entry for each thread
        container->push_back( newnode );

        // Register instance with per-thread instance of enclosing subnet.
        static_cast< Subnet* >( subnet_container->get_thread_sibling( t ) )
          ->add_node( newnode );
      }
    }
  }
  else
  {
    // no proxies and one node per process
    // this is used by MUSIC proxies
    // Per r9700, this case is only relevant for music_*_proxy models,
    // which have a single instance per MPI process.
    for ( index gid = min_gid; gid < max_gid; ++gid )
    {
      Node* newnode = model->allocate( 0 );
      newnode->set_gid_( gid );
      newnode->set_model_id( mod );
      newnode->set_thread( 0 );
      newnode->set_vp( kernel().vp_manager.thread_to_vp( 0 ) );

      // Register instance
      local_nodes_.add_local_node( *newnode );

      // and into current subnet, thread 0.
      current_->add_node( newnode );
    }
  }

  // set off-grid spike communication if necessary
  if ( model->is_off_grid() )
  {
    kernel().event_delivery_manager.set_off_grid_communication( true );
    LOG( M_INFO,
      "NodeManager::add_node",
      "Neuron models emitting precisely timed spikes exist: "
      "the kernel property off_grid_spiking has been set to true.\n\n"
      "NOTE: Mixing precise-spiking and normal neuron models may "
      "lead to inconsistent results." );
  }

  return max_gid - 1;
}
void
nest::SimulationManager::set_status( const DictionaryDatum& d )
{
    // Create an instance of time converter here to capture the current
    // representation of time objects: TICS_PER_MS and TICS_PER_STEP
    // will be stored in time_converter.
    // This object can then be used to convert times in steps
    // (e.g. Connection::delay_) or tics to the new representation.
    // We pass this object to ConnectionManager::calibrate to update
    // all time objects in the connection system to the new representation.
    // MH 08-04-14
    TimeConverter time_converter;

    double_t time;
    if ( updateValue< double_t >( d, "time", time ) )
    {
        if ( time != 0.0 )
            throw BadProperty( "The simulation time can only be set to 0.0." );

        if ( clock_ > TimeZero )
        {
            // reset only if time has passed
            LOG( M_WARNING,
                 "SimulationManager::set_status",
                 "Simulation time reset to t=0.0. Resetting the simulation time is not "
                 "fully supported in NEST at present. Some spikes may be lost, and "
                 "stimulating devices may behave unexpectedly. PLEASE REVIEW YOUR "
                 "SIMULATION OUTPUT CAREFULLY!" );

            clock_ = Time::step( 0 );
            from_step_ = 0;
            slice_ = 0;
            // clear all old spikes
            kernel().event_delivery_manager.configure_spike_buffers();
        }
    }

    updateValue< bool >( d, "print_time", print_time_ );

    // tics_per_ms and resolution must come after local_num_thread /
    // total_num_threads because they might reset the network and the time
    // representation
    nest::double_t tics_per_ms = 0.0;
    bool tics_per_ms_updated =
        updateValue< nest::double_t >( d, "tics_per_ms", tics_per_ms );
    double_t resd = 0.0;
    bool res_updated = updateValue< double_t >( d, "resolution", resd );

    if ( tics_per_ms_updated || res_updated )
    {
        if ( kernel().node_manager.size() > 1 ) // root always exists
        {
            LOG( M_ERROR,
                 "SimulationManager::set_status",
                 "Cannot change time representation after nodes have been created. "
                 "Please call ResetKernel first." );
            throw KernelException();
        }
        else if ( has_been_simulated() ) // someone may have simulated empty network
        {
            LOG( M_ERROR,
                 "SimulationManager::set_status",
                 "Cannot change time representation after the network has been "
                 "simulated. Please call ResetKernel first." );
            throw KernelException();
        }
        else if ( kernel().connection_manager.get_num_connections() != 0 )
        {
            LOG( M_ERROR,
                 "SimulationManager::set_status",
                 "Cannot change time representation after connections have been "
                 "created. Please call ResetKernel first." );
            throw KernelException();
        }
        else if ( res_updated && tics_per_ms_updated ) // only allow TICS_PER_MS to
            // be changed together with
            // resolution
        {
            if ( resd < 1.0 / tics_per_ms )
            {
                LOG( M_ERROR,
                     "SimulationManager::set_status",
                     "Resolution must be greater than or equal to one tic. Value "
                     "unchanged." );
                throw KernelException();
            }
            else
            {
                nest::Time::set_resolution( tics_per_ms, resd );
                // adjust to new resolution
                clock_.calibrate();
                // adjust delays in the connection system to new resolution
                kernel().connection_manager.calibrate( time_converter );
                kernel().model_manager.calibrate( time_converter );
                LOG( M_INFO,
                     "SimulationManager::set_status",
                     "tics per ms and resolution changed." );

                // make sure that wfr communication interval is always greater or equal
                // to resolution if no wfr is used explicitly set wfr_comm_interval
                // to resolution because communication in every step is needed
                if ( wfr_comm_interval_ < Time::get_resolution().get_ms()
                        || not use_wfr_ )
                {
                    wfr_comm_interval_ = Time::get_resolution().get_ms();
                }
            }
        }
        else if ( res_updated ) // only resolution changed
        {
            if ( resd < Time::get_ms_per_tic() )
            {
                LOG( M_ERROR,
                     "SimulationManager::set_status",
                     "Resolution must be greater than or equal to one tic. Value "
                     "unchanged." );
                throw KernelException();
            }
            else
            {
                Time::set_resolution( resd );
                clock_.calibrate(); // adjust to new resolution
                // adjust delays in the connection system to new resolution
                kernel().connection_manager.calibrate( time_converter );
                kernel().model_manager.calibrate( time_converter );
                LOG( M_INFO,
                     "SimulationManager::set_status",
                     "Temporal resolution changed." );

                // make sure that wfr communication interval is always greater or equal
                // to resolution if no wfr is used explicitly set wfr_comm_interval
                // to resolution because communication in every step is needed
                if ( wfr_comm_interval_ < Time::get_resolution().get_ms()
                        || not use_wfr_ )
                {
                    wfr_comm_interval_ = Time::get_resolution().get_ms();
                }
            }
        }
        else
        {
            LOG( M_ERROR,
                 "SimulationManager::set_status",
                 "change of tics_per_step requires simultaneous specification of "
                 "resolution." );
            throw KernelException();
        }
    }

    // The decision whether the waveform relaxation is used
    // must be set before nodes are created.
    // Important: wfr_comm_interval_ may change depending on use_wfr_
    bool wfr;
    if ( updateValue< bool >( d, "use_wfr", wfr ) )
    {
        if ( kernel().node_manager.size() > 1 )
        {
            LOG( M_ERROR,
                 "SimulationManager::set_status",
                 "Cannot enable/disable usage of waveform relaxation after nodes have "
                 "been created. Please call ResetKernel first." );
            throw KernelException();
        }
        else
        {
            use_wfr_ = wfr;
            // if no wfr is used explicitly set wfr_comm_interval to resolution
            // because communication in every step is needed
            if ( not use_wfr_ )
            {
                wfr_comm_interval_ = Time::get_resolution().get_ms();
            }
        }
    }

    // wfr_comm_interval_ can only be changed if use_wfr_ is true and before
    // connections are created. If use_wfr_ is false wfr_comm_interval_ is set to
    // the resolution whenever the resolution changes.
    double_t wfr_interval;
    if ( updateValue< double_t >( d, "wfr_comm_interval", wfr_interval ) )
    {
        if ( not use_wfr_ )
        {
            LOG( M_ERROR,
                 "SimulationManager::set_status",
                 "Cannot set waveform communication interval when usage of waveform "
                 "relaxation is disabled. Set use_wfr to true first." );
            throw KernelException();
        }
        else if ( kernel().connection_manager.get_num_connections() != 0 )
        {
            LOG( M_ERROR,
                 "SimulationManager::set_status",
                 "Cannot change waveform communication interval after connections have "
                 "been created. Please call ResetKernel first." );
            throw KernelException();
        }
        else if ( wfr_interval < Time::get_resolution().get_ms() )
        {
            LOG( M_ERROR,
                 "SimulationManager::set_status",
                 "Communication interval of the waveform relaxation must be greater or "
                 "equal to the resolution of the simulation." );
            throw KernelException();
        }
        else
        {
            LOG( M_INFO,
                 "SimulationManager::set_status",
                 "Waveform communication interval changed successfully. " );
            wfr_comm_interval_ = wfr_interval;
        }
    }

    // set the convergence tolerance for the waveform relaxation method
    double_t tol;
    if ( updateValue< double_t >( d, "wfr_tol", tol ) )
    {
        if ( tol < 0.0 )
            LOG( M_ERROR,
                 "SimulationManager::set_status",
                 "Tolerance must be zero or positive" );
        else
            wfr_tol_ = tol;
    }

    // set the maximal number of iterations for the waveform relaxation method
    long max_iter;
    if ( updateValue< long >( d, "wfr_max_iterations", max_iter ) )
    {
        if ( max_iter <= 0 )
            LOG( M_ERROR,
                 "SimulationManager::set_status",
                 "Maximal number of iterations  for the waveform relaxation must be "
                 "positive. To disable waveform relaxation set use_wfr instead." );
        else
            wfr_max_iterations_ = max_iter;
    }

    // set the interpolation order for the waveform relaxation method
    long interp_order;
    if ( updateValue< long >( d, "wfr_interpolation_order", interp_order ) )
    {
        if ( ( interp_order < 0 ) || ( interp_order == 2 ) || ( interp_order > 3 ) )
            LOG( M_ERROR,
                 "SimulationManager::set_status",
                 "Interpolation order must be 0, 1, or 3." );
        else
            wfr_interpolation_order_ = interp_order;
    }
}
예제 #29
0
void nest::spike_generator::Parameters_::set(const DictionaryDatum& d, State_& s,
        const Time& origin, const Time& now)
{
    const bool flags_changed =    updateValue<bool>(d, names::precise_times, precise_times_)
                                  || updateValue<bool>(d, "allow_offgrid_spikes", allow_offgrid_spikes_)
                                  || updateValue<bool>(d, "shift_now_spikes", shift_now_spikes_);
    if ( precise_times_ && ( allow_offgrid_spikes_ || shift_now_spikes_ ) )
        throw BadProperty("Option precise_times cannot be set to true when either allow_offgrid_spikes "
                          "or shift_now_spikes is set to true.");

    const bool updated_spike_times = d->known(names::spike_times);
    if ( flags_changed && !(updated_spike_times || spike_stamps_.empty()) )
        throw BadProperty("Options can only be set together with spike times or if no "
                          "spike times have been set.");

    if ( updated_spike_times )
    {
        const std::vector<double_t> d_times = getValue<std::vector<double> >(d->lookup(names::spike_times));
        const size_t n_spikes = d_times.size();
        spike_stamps_.clear();
        spike_stamps_.reserve(n_spikes);
        spike_offsets_.clear();
        if ( precise_times_ )
            spike_offsets_.reserve(n_spikes);

        // Check spike times for ordering and grid compatibility and insert them
        if ( !d_times.empty() )
        {
            // handle first spike time, no predecessor to compare with
            std::vector<double_t>::const_iterator prev = d_times.begin();
            assert_valid_spike_time_and_insert_(*prev, origin, now);

            // handle all remaining spike times, compare to predecessor
            for ( std::vector<double_t>::const_iterator next = prev + 1;
                    next != d_times.end() ; ++next, ++prev )
                if ( *prev > *next )
                    throw BadProperty("Spike times must be sorted in non-descending order.");
                else
                    assert_valid_spike_time_and_insert_(*next, origin, now);
        }
    }

    // spike_weights can be the same size as spike_times, or can be of size 0 to
    // only use the spike_times array
    bool updated_spike_weights = d->known("spike_weights");
    if (updated_spike_weights)
    {
        std::vector<double> spike_weights = getValue<std::vector<double> >(d->lookup("spike_weights"));

        if (spike_weights.empty())
            spike_weights_.clear();
        else
        {
            if (spike_weights.size() != spike_stamps_.size())
                throw BadProperty("spike_weights must have the same number of elements as spike_times,"
                                  " or 0 elements to clear the property.");

            spike_weights_.swap(spike_weights);
        }
    }

    // Set position to start if something changed
    if ( updated_spike_times || updated_spike_weights || d->known(names::origin) )
        s.position_ = 0;
}
예제 #30
0
void
nest::iaf_tum_2000::calibrate()
{
  B_.logger_.init();

  const double h = Time::get_resolution().get_ms();

  // numbering of state vaiables: i_0 = 0, i_syn_ = 1, V_m_ = 2

  // commented out propagators: forward Euler
  // needed to exactly reproduce Tsodyks network

  // these P are independent
  V_.P11ex_ = std::exp( -h / P_.tau_ex_ );
  // P11ex_ = 1.0-h/tau_ex_;

  V_.P11in_ = std::exp( -h / P_.tau_in_ );
  // P11in_ = 1.0-h/tau_in_;

  V_.P22_ = std::exp( -h / P_.Tau_ );
  // P22_ = 1.0-h/Tau_;

  // these are determined according to a numeric stability criterion
  V_.P21ex_ = propagator_32( P_.tau_ex_, P_.Tau_, P_.C_, h );
  V_.P21in_ = propagator_32( P_.tau_in_, P_.Tau_, P_.C_, h );

  // P21ex_ = h/C_;
  // P21in_ = h/C_;

  V_.P20_ = P_.Tau_ / P_.C_ * ( 1.0 - V_.P22_ );
  // P20_ = h/C_;

  // tau_ref_abs_ and tau_ref_tot_ specify the length of the corresponding
  // refractory periods as doubles in ms. The grid based iaf_tum_2000 can
  // only handle refractory periods that are integer multiples of the
  // computation step size (h). To ensure consistency with the overall
  // simulation scheme such conversion should be carried out via objects of
  // class nest::Time. The conversion requires 2 steps:
  //     1. A time object r is constructed, defining representation of
  //        tau_ref_{abs,tot} in tics. This representation is then converted
  //        to computation time steps again by a strategy defined by class
  //        nest::Time.
  //     2. The refractory time in units of steps is read out get_steps(), a
  //        member function of class nest::Time.
  //
  // Choosing a tau_ref_{abs,tot} that is not an integer multiple of the
  // computation time step h will lead to accurate (up to the resolution h)
  // and self- consistent results. However, a neuron model capable of
  // operating with real valued spike time may exhibit a different
  // effective refractory time.

  V_.RefractoryCountsAbs_ = Time( Time::ms( P_.tau_ref_abs_ ) ).get_steps();

  V_.RefractoryCountsTot_ = Time( Time::ms( P_.tau_ref_tot_ ) ).get_steps();

  if ( V_.RefractoryCountsAbs_ < 1 )
  {
    throw BadProperty(
      "Absolute refractory time must be at least one time step." );
  }

  if ( V_.RefractoryCountsTot_ < 1 )
  {
    throw BadProperty(
      "Total refractory time must be at least one time step." );
  }
}