/*
* pif_psc_alpha.cpp
*
* This file is part of NEST.
*
* Copyright (C) 2004 The NEST Initiative
*
* NEST is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* NEST is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NEST. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "exceptions.h"
#include "pif_psc_alpha.h"
#include "network.h"
#include "dict.h"
#include "integerdatum.h"
#include "doubledatum.h"
#include "dictutils.h"
#include "numerics.h"
#include "universal_data_logger_impl.h"
#include "lockptrdatum.h"
#include <limits>
using namespace nest;
/* ----------------------------------------------------------------
* Recordables map
* ---------------------------------------------------------------- */
nest::RecordablesMap<mynest::pif_psc_alpha> mynest::pif_psc_alpha::recordablesMap_;
namespace nest
{
// Override the create() method with one call to RecordablesMap::insert_()
// for each quantity to be recorded.
template <>
void RecordablesMap<mynest::pif_psc_alpha>::create()
{
// use standard names whereever you can for consistency!
insert_(names::V_m, &mynest::pif_psc_alpha::get_V_m_);
}
}
/* ----------------------------------------------------------------
* Default constructors defining default parameters and state
* ---------------------------------------------------------------- */
mynest::pif_psc_alpha::Parameters_::Parameters_()
: C_m (250.0), // pF
I_e ( 0.0), // nA
tau_syn( 2.0), // ms
V_th (-55.0), // mV
V_reset(-70.0), // mV
t_ref ( 2.0) // ms
{}
mynest::pif_psc_alpha::State_::State_(const Parameters_& p)
: V_m (p.V_reset),
dI_syn (0.0),
I_syn (0.0),
I_ext (0.0),
refr_count(0 )
{}
/* ----------------------------------------------------------------
* Parameter and state extractions and manipulation functions
* ---------------------------------------------------------------- */
void mynest::pif_psc_alpha::Parameters_::get(DictionaryDatum &d) const
{
(*d)[names::C_m ] = C_m;
(*d)[names::I_e ] = I_e;
(*d)[names::tau_syn] = tau_syn;
(*d)[names::V_th ] = V_th;
(*d)[names::V_reset] = V_reset;
(*d)[names::t_ref ] = t_ref;
}
void mynest::pif_psc_alpha::Parameters_::set(const DictionaryDatum& d)
{
updateValue<double>(d, names::C_m, C_m);
updateValue<double>(d, names::I_e, I_e);
updateValue<double>(d, names::tau_syn, tau_syn);
updateValue<double>(d, names::V_th , V_th);
updateValue<double>(d, names::V_reset, V_reset);
updateValue<double>(d, names::t_ref, t_ref);
if ( C_m <= 0 )
throw nest::BadProperty("The membrane capacitance must be strictly positive.");
if ( tau_syn <= 0 )
throw nest::BadProperty("The synaptic time constant must be strictly positive.");
if ( V_reset >= V_th )
throw nest::BadProperty("The reset potential must be below threshold.");
if ( t_ref < 0 )
throw nest::BadProperty("The refractory time must be at least one simulation step.");
}
void mynest::pif_psc_alpha::State_::get(DictionaryDatum &d) const
{
// Only the membrane potential is shown in the status; one could show also the other
// state variables
(*d)[names::V_m] = V_m;
}
void mynest::pif_psc_alpha::State_::set(const DictionaryDatum& d, const Parameters_& p)
{
// Only the membrane potential can be set; one could also make other state variables
// settable.
updateValue<double>(d, names::V_m, V_m);
}
mynest::pif_psc_alpha::Buffers_::Buffers_(pif_psc_alpha &n)
: logger_(n)
{}
mynest::pif_psc_alpha::Buffers_::Buffers_(const Buffers_ &, pif_psc_alpha &n)
: logger_(n)
{}
/* ----------------------------------------------------------------
* Default and copy constructor for node
* ---------------------------------------------------------------- */
mynest::pif_psc_alpha::pif_psc_alpha()
: Node(),
P_(),
S_(P_),
B_(*this)
{
recordablesMap_.create();
}
mynest::pif_psc_alpha::pif_psc_alpha(const pif_psc_alpha& n)
: Node(n),
P_(n.P_),
S_(n.S_),
B_(n.B_, *this)
{}
/* ----------------------------------------------------------------
* Node initialization functions
* ---------------------------------------------------------------- */
void mynest::pif_psc_alpha::init_state_(const Node& proto)
{
const pif_psc_alpha& pr = downcast<pif_psc_alpha>(proto);
S_ = pr.S_;
}
void mynest::pif_psc_alpha::init_buffers_()
{
B_.spikes.clear(); // includes resize
B_.currents.clear(); // include resize
B_.logger_.reset(); // includes resize
}
void mynest::pif_psc_alpha::calibrate()
{
B_.logger_.init();
const double h = Time::get_resolution().get_ms();
const double eh = std::exp(-h/P_.tau_syn);
const double tc = P_.tau_syn / P_.C_m;
// compute matrix elements, all other elements 0
V_.P11 = eh;
V_.P22 = eh;
V_.P21 = h * eh;
V_.P30 = h / P_.C_m;
V_.P31 = tc * ( P_.tau_syn - (h+P_.tau_syn) * eh );
V_.P32 = tc * ( 1 - eh );
// P33_ is 1
// initial value ensure normalization to max amplitude 1.0
V_.pscInitialValue = 1.0 * numerics::e / P_.tau_syn;
// refractory time in steps
V_.t_ref_steps = Time(Time::ms(P_.t_ref)).get_steps();
assert(V_.t_ref_steps >= 0); // since t_ref_ >= 0, this can only fail in error
}
/* ----------------------------------------------------------------
* Update and spike handling functions
* ---------------------------------------------------------------- */
void mynest::pif_psc_alpha::update(Time const& slice_origin,
const nest::long_t from_step,
const nest::long_t to_step)
{
for ( long lag = from_step ; lag < to_step ; ++lag )
{
// order is important in this loop, since we have to use the old values
// (those upon entry to the loop) on right hand sides everywhere
// update membrane potential
if ( S_.refr_count == 0 ) // neuron absolute not refractory
S_.V_m += V_.P30 * ( S_.I_ext + P_.I_e )
+ V_.P31 * S_.dI_syn
+ V_.P32 * S_.I_syn;
else
--S_.refr_count; // count down refractory time
// update synaptic currents
S_.I_syn = V_.P21 * S_.dI_syn + V_.P22 * S_.I_syn;
S_.dI_syn *= V_.P11;
// check for threshold crossing
if ( S_.V_m >= P_.V_th )
{
// reset neuron
S_.refr_count = V_.t_ref_steps;
S_.V_m = P_.V_reset;
// send spike
SpikeEvent se;
network()->send(*this, se, lag);
}
// add synaptic input currents for this step
S_.dI_syn += V_.pscInitialValue * B_.spikes.get_value(lag);
// set new input current
S_.I_ext = B_.currents.get_value(lag);
// log membrane potential
B_.logger_.record_data(slice_origin.get_steps()+lag);
}
}
void mynest::pif_psc_alpha::handle(SpikeEvent & e)
{
assert(e.get_delay() > 0);
B_.spikes.add_value(e.get_rel_delivery_steps(network()->get_slice_origin()),
e.get_weight());
}
void mynest::pif_psc_alpha::handle(CurrentEvent& e)
{
assert(e.get_delay() > 0);
B_.currents.add_value(e.get_rel_delivery_steps(network()->get_slice_origin()),
e.get_weight() * e.get_current());
}
// Do not move this function as inline to h-file. It depends on
// universal_data_logger_impl.h being included here.
void mynest::pif_psc_alpha::handle(DataLoggingRequest& e)
{
B_.logger_.handle(e); // the logger does this for us
}