/**
 *  hhca_psc_alpha.h
 *
 *  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/>.
 *
 *  Generated from NESTML at time: 2021-09-17 08:49:06.519180
**/
#ifndef HHCA_PSC_ALPHA
#define HHCA_PSC_ALPHA

#include "config.h"

#ifndef HAVE_GSL
#error "The GSL library is required for neurons that require a numerical solver."
#endif

// External includes:
#include <gsl/gsl_errno.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_odeiv.h>

// Includes from nestkernel:
#include "archiving_node.h"
#include "connection.h"
#include "event.h"
#include "nest_types.h"
#include "ring_buffer.h"
#include "universal_data_logger.h"

// Includes from sli:
#include "dictdatum.h"


/**
 * Function computing right-hand side of ODE for GSL solver.
 * @note Must be declared here so we can befriend it in class.
 * @note Must have C-linkage for passing to GSL. Internally, it is
 *       a first-class C++ function, but cannot be a member function
 *       because of the C-linkage.
 * @note No point in declaring it inline, since it is called
 *       through a function pointer.
 * @param void* Pointer to model neuron instance.
**/
extern "C" inline int hhca_psc_alpha_dynamics( double, const double y[], double f[], void* pnode );


/* BeginDocumentation
  Name: hhca_psc_alpha.

  Description:

    """
  Name: hhca_psc_alpha - Hodgkin Huxley neuron model.

  Description:

   hhca_psc_alpha is an implementation of a spiking neuron using the Hodkin-Huxley
   formalism.

   (1) Post-syaptic currents
   Incoming spike events induce a post-synaptic change of current modelled
   by an alpha function. The alpha function is normalised such that an event of
   weight 1.0 results in a peak current of 1 pA.


   (2) Spike Detection
   Spike detection is done by a combined threshold-and-local-maximum search: if
   there is a local maximum above a certain threshold of the membrane potential,
   it is considered a spike.

  Problems/Todo:

   better spike detection
   initial wavelet/spike at simulation onset

  References:

   Spiking Neuron Models:
   Single Neurons, Populations, Plasticity
   Wulfram Gerstner, Werner Kistler,  Cambridge University Press

   Theoretical Neuroscience:
   Computational and Mathematical Modeling of Neural Systems
   Peter Dayan, L. F. Abbott, MIT Press (parameters taken from here)

   Hodgkin, A. L. and Huxley, A. F.,
   A Quantitative Description of Membrane Current
   and Its Application to Conduction and Excitation in Nerve,
   Journal of Physiology, 117, 500-544 (1952)

  Sends: SpikeEvent

  Receives: SpikeEvent, CurrentEvent, DataLoggingRequest

  Authors: Tanguy Fardet
  SeeAlso: hh_psc_alpha
  """


  Parameters:
  The following parameters can be set in the status dictionary.
t_ref [ms]  Refractory period
g_Na [nS]  Sodium peak conductance
g_K [nS]  Potassium peak conductance
g_L [nS]  Leak conductance
C_m [pF]  Membrane Capacitance
E_Na [mV]  Sodium reversal potential
E_K [mV]  Potassium reversal potential
E_L [mV]  Leak reversal Potential (aka resting potential)
tau_syn_ex [ms]  Rise time of the excitatory synaptic alpha function i
tau_syn_in [ms]  Rise time of the inhibitory synaptic alpha function
I_e [pA]  constant external input current


  Dynamic state variables:
r [integer]  number of steps in the current refractory phase
V_m [mV]  Membrane potential
Act_m [real]  Activation variable m for Na
Inact_h [real]  Inactivation variable h for Na
Act_n [real]  Activation variable n for K


  Sends: nest::SpikeEvent

  Receives: Spike, Current, DataLoggingRequest
*/
class hhca_psc_alpha : public nest::ArchivingNode
{
public:
  /**
   * The constructor is only used to create the model prototype in the model manager.
  **/
  hhca_psc_alpha();

  /**
   * The copy constructor is used to create model copies and instances of the model.
   * @node The copy constructor needs to initialize the parameters and the state.
   *       Initialization of buffers and interal variables is deferred to
   *       @c init_buffers_() and @c calibrate().
  **/
  hhca_psc_alpha(const hhca_psc_alpha &);

  /**
   * Destructor.
  **/
  ~hhca_psc_alpha();

  // -------------------------------------------------------------------------
  //   Import sets of overloaded virtual functions.
  //   See: Technical Issues / Virtual Functions: Overriding, Overloading,
  //        and Hiding
  // -------------------------------------------------------------------------

  using nest::Node::handles_test_event;
  using nest::Node::handle;

  /**
   * Used to validate that we can send nest::SpikeEvent to desired target:port.
  **/
  nest::port send_test_event(nest::Node& target, nest::rport receptor_type, nest::synindex, bool);

  // -------------------------------------------------------------------------
  //   Functions handling incoming events.
  //   We tell nest that we can handle incoming events of various types by
  //   defining handle() for the given event.
  // -------------------------------------------------------------------------


  void handle(nest::SpikeEvent &);        //! accept spikes
  void handle(nest::CurrentEvent &);      //! accept input current
  void handle(nest::DataLoggingRequest &);//! allow recording with multimeter
  nest::port handles_test_event(nest::SpikeEvent&, nest::port);
  nest::port handles_test_event(nest::CurrentEvent&, nest::port);
  nest::port handles_test_event(nest::DataLoggingRequest&, nest::port);

  // -------------------------------------------------------------------------
  //   Functions for getting/setting parameters and state values.
  // -------------------------------------------------------------------------

  void get_status(DictionaryDatum &) const;
  void set_status(const DictionaryDatum &);

private:

  /**
   * Reset internal buffers of neuron.
  **/
  void init_buffers_();

  /**
   * Initialize auxiliary quantities, leave parameters and state untouched.
  **/
  void calibrate();

  /**
   * Take neuron through given time interval
  **/
  void update(nest::Time const &, const long, const long);

  // The next two classes need to be friends to access the State_ class/member
  friend class nest::RecordablesMap<hhca_psc_alpha>;
  friend class nest::UniversalDataLogger<hhca_psc_alpha>;

  /**
   * Free parameters of the neuron.
   *
   *
   *
   * These are the parameters that can be set by the user through @c `node.set()`.
   * They are initialized from the model prototype when the node is created.
   * Parameters do not change during calls to @c update() and are not reset by
   * @c ResetNetwork.
   *
   * @note Parameters_ need neither copy constructor nor @c operator=(), since
   *       all its members are copied properly by the default copy constructor
   *       and assignment operator. Important:
   *       - If Parameters_ contained @c Time members, you need to define the
   *         assignment operator to recalibrate all members of type @c Time . You
   *         may also want to define the assignment operator.
   *       - If Parameters_ contained members that cannot copy themselves, such
   *         as C-style arrays, you need to define the copy constructor and
   *         assignment operator to copy those members.
  **/
  struct Parameters_
  {    
    //!  Refractory period
    double t_ref;
    //!  Sodium peak conductance
    double g_Na;
    //!  Potassium peak conductance
    double g_K;
    //!  Leak conductance
    double g_L;
    //!  Membrane Capacitance
    double C_m;
    //!  Sodium reversal potential
    double E_Na;
    //!  Potassium reversal potential
    double E_K;
    //!  Leak reversal Potential (aka resting potential)
    double E_L;
    //!  Rise time of the excitatory synaptic alpha function i
    double tau_syn_ex;
    //!  Rise time of the inhibitory synaptic alpha function
    double tau_syn_in;
    double g_Ca;
    double Ca_env;
    double Ca_i_eq;
    double tau_Ca;
    double V_hmCa;
    double k_mCa;
    double tau_mCa;
    double V_hhCa;
    double k_hCa;
    double tau_hCa;
    double g_AHP;
    double K_AHP;
    double b_AHP;
    double tau_AHP;
    double E_0;
    double Vcell;
    //!  constant external input current
    double I_e;

    double __gsl_error_tol;

    /**
     * Initialize parameters to their default values.
    **/
    Parameters_();
  };

  /**
   * Dynamic state of the neuron.
   *
   *
   *
   * These are the state variables that are advanced in time by calls to
   * @c update(). In many models, some or all of them can be set by the user
   * through @c `node.set()`. The state variables are initialized from the model
   * prototype when the node is created. State variables are reset by @c ResetNetwork.
   *
   * @note State_ need neither copy constructor nor @c operator=(), since
   *       all its members are copied properly by the default copy constructor
   *       and assignment operator. Important:
   *       - If State_ contained @c Time members, you need to define the
   *         assignment operator to recalibrate all members of type @c Time . You
   *         may also want to define the assignment operator.
   *       - If State_ contained members that cannot copy themselves, such
   *         as C-style arrays, you need to define the copy constructor and
   *         assignment operator to copy those members.
  **/
  struct State_
  {
    //! Symbolic indices to the elements of the state vector y
    enum StateVecElems
    {
      V_m,
      Ca_i,
      Act_n,
      Act_m,
      Inact_h,
      h_Ca,
      m_Ca,
      m_AHP,
      I_syn_ex__X__spikeExc,
      I_syn_ex__X__spikeExc__d,
      I_syn_in__X__spikeInh,
      I_syn_in__X__spikeInh__d,
      r,
      alpha_n_init,
      beta_n_init,
      alpha_m_init,
      beta_m_init,
      alpha_h_init,
      beta_h_init,
      STATE_VEC_SIZE
    };

    //! state vector, must be C-array for GSL solver
    double ode_state[STATE_VEC_SIZE];

    State_();
  };

  /**
   * Internal variables of the neuron.
   *
   *
   *
   * These variables must be initialized by @c calibrate, which is called before
   * the first call to @c update() upon each call to @c Simulate.
   * @node Variables_ needs neither constructor, copy constructor or assignment operator,
   *       since it is initialized by @c calibrate(). If Variables_ has members that
   *       cannot destroy themselves, Variables_ will need a destructor.
  **/
  struct Variables_
  {
    //!  refractory time in steps
    long RefractoryCounts;
    double __h;
    double charge;
    double avogadro;
    //!  convert charge flux to concentration roughly 1e-2 mmol/L/fC
    double c2c;
    double __P__I_syn_ex__X__spikeExc__I_syn_ex__X__spikeExc;
    double __P__I_syn_ex__X__spikeExc__I_syn_ex__X__spikeExc__d;
    double __P__I_syn_ex__X__spikeExc__d__I_syn_ex__X__spikeExc;
    double __P__I_syn_ex__X__spikeExc__d__I_syn_ex__X__spikeExc__d;
    double __P__I_syn_in__X__spikeInh__I_syn_in__X__spikeInh;
    double __P__I_syn_in__X__spikeInh__I_syn_in__X__spikeInh__d;
    double __P__I_syn_in__X__spikeInh__d__I_syn_in__X__spikeInh;
    double __P__I_syn_in__X__spikeInh__d__I_syn_in__X__spikeInh__d;
  };

  /**
   * Buffers of the neuron.
   * Usually buffers for incoming spikes and data logged for analog recorders.
   * Buffers must be initialized by @c init_buffers_(), which is called before
   * @c calibrate() on the first call to @c Simulate after the start of NEST,
   * ResetKernel or ResetNetwork.
   * @node Buffers_ needs neither constructor, copy constructor or assignment operator,
   *       since it is initialized by @c init_nodes_(). If Buffers_ has members that
   *       cannot destroy themselves, Buffers_ will need a destructor.
  **/
  struct Buffers_
  {
    Buffers_(hhca_psc_alpha &);
    Buffers_(const Buffers_ &, hhca_psc_alpha &);

    /**
     * Logger for all analog data
    **/
    nest::UniversalDataLogger<hhca_psc_alpha> logger_;

    inline nest::RingBuffer& get_spikeInh() {return spikeInh;}
    //!< Buffer for input (type: pA)
    nest::RingBuffer spikeInh;
    double spikeInh_grid_sum_;
    inline nest::RingBuffer& get_spikeExc() {return spikeExc;}
    //!< Buffer for input (type: pA)
    nest::RingBuffer spikeExc;
    double spikeExc_grid_sum_;
    //!< Buffer for input (type: pA)
    nest::RingBuffer I_stim;
    inline nest::RingBuffer& get_I_stim() {return I_stim;}
    double I_stim_grid_sum_;

    // -----------------------------------------------------------------------
    //   GSL ODE solver data structures
    // -----------------------------------------------------------------------
    
    gsl_odeiv_step* __s;    //!< stepping function
    gsl_odeiv_control* __c; //!< adaptive stepsize control function
    gsl_odeiv_evolve* __e;  //!< evolution function
    gsl_odeiv_system __sys; //!< struct describing system

    // __integration_step should be reset with the neuron on ResetNetwork,
    // but remain unchanged during calibration. Since it is initialized with
    // step_, and the resolution cannot change after nodes have been created,
    // it is safe to place both here.
    double __step;             //!< step size in ms
    double __integration_step; //!< current integration time step, updated by GSL
  };

  // -------------------------------------------------------------------------
  //   Getters/setters for state block
  // -------------------------------------------------------------------------

  inline long get_r() const
  {
    return S_.ode_state[State_::r];
  }

  inline void set_r(const long __v)
  {
    S_.ode_state[State_::r] = __v;
  }

  inline double get_V_m() const
  {
    return S_.ode_state[State_::V_m];
  }

  inline void set_V_m(const double __v)
  {
    S_.ode_state[State_::V_m] = __v;
  }

  inline double get_Ca_i() const
  {
    return S_.ode_state[State_::Ca_i];
  }

  inline void set_Ca_i(const double __v)
  {
    S_.ode_state[State_::Ca_i] = __v;
  }

  inline double get_alpha_n_init() const
  {
    return S_.ode_state[State_::alpha_n_init];
  }

  inline void set_alpha_n_init(const double __v)
  {
    S_.ode_state[State_::alpha_n_init] = __v;
  }

  inline double get_beta_n_init() const
  {
    return S_.ode_state[State_::beta_n_init];
  }

  inline void set_beta_n_init(const double __v)
  {
    S_.ode_state[State_::beta_n_init] = __v;
  }

  inline double get_alpha_m_init() const
  {
    return S_.ode_state[State_::alpha_m_init];
  }

  inline void set_alpha_m_init(const double __v)
  {
    S_.ode_state[State_::alpha_m_init] = __v;
  }

  inline double get_beta_m_init() const
  {
    return S_.ode_state[State_::beta_m_init];
  }

  inline void set_beta_m_init(const double __v)
  {
    S_.ode_state[State_::beta_m_init] = __v;
  }

  inline double get_alpha_h_init() const
  {
    return S_.ode_state[State_::alpha_h_init];
  }

  inline void set_alpha_h_init(const double __v)
  {
    S_.ode_state[State_::alpha_h_init] = __v;
  }

  inline double get_beta_h_init() const
  {
    return S_.ode_state[State_::beta_h_init];
  }

  inline void set_beta_h_init(const double __v)
  {
    S_.ode_state[State_::beta_h_init] = __v;
  }

  inline double get_Act_m() const
  {
    return S_.ode_state[State_::Act_m];
  }

  inline void set_Act_m(const double __v)
  {
    S_.ode_state[State_::Act_m] = __v;
  }

  inline double get_Inact_h() const
  {
    return S_.ode_state[State_::Inact_h];
  }

  inline void set_Inact_h(const double __v)
  {
    S_.ode_state[State_::Inact_h] = __v;
  }

  inline double get_Act_n() const
  {
    return S_.ode_state[State_::Act_n];
  }

  inline void set_Act_n(const double __v)
  {
    S_.ode_state[State_::Act_n] = __v;
  }

  inline double get_h_Ca() const
  {
    return S_.ode_state[State_::h_Ca];
  }

  inline void set_h_Ca(const double __v)
  {
    S_.ode_state[State_::h_Ca] = __v;
  }

  inline double get_m_Ca() const
  {
    return S_.ode_state[State_::m_Ca];
  }

  inline void set_m_Ca(const double __v)
  {
    S_.ode_state[State_::m_Ca] = __v;
  }

  inline double get_m_AHP() const
  {
    return S_.ode_state[State_::m_AHP];
  }

  inline void set_m_AHP(const double __v)
  {
    S_.ode_state[State_::m_AHP] = __v;
  }

  inline double get_I_syn_ex__X__spikeExc() const
  {
    return S_.ode_state[State_::I_syn_ex__X__spikeExc];
  }

  inline void set_I_syn_ex__X__spikeExc(const double __v)
  {
    S_.ode_state[State_::I_syn_ex__X__spikeExc] = __v;
  }

  inline double get_I_syn_ex__X__spikeExc__d() const
  {
    return S_.ode_state[State_::I_syn_ex__X__spikeExc__d];
  }

  inline void set_I_syn_ex__X__spikeExc__d(const double __v)
  {
    S_.ode_state[State_::I_syn_ex__X__spikeExc__d] = __v;
  }

  inline double get_I_syn_in__X__spikeInh() const
  {
    return S_.ode_state[State_::I_syn_in__X__spikeInh];
  }

  inline void set_I_syn_in__X__spikeInh(const double __v)
  {
    S_.ode_state[State_::I_syn_in__X__spikeInh] = __v;
  }

  inline double get_I_syn_in__X__spikeInh__d() const
  {
    return S_.ode_state[State_::I_syn_in__X__spikeInh__d];
  }

  inline void set_I_syn_in__X__spikeInh__d(const double __v)
  {
    S_.ode_state[State_::I_syn_in__X__spikeInh__d] = __v;
  }


  // -------------------------------------------------------------------------
  //   Getters/setters for parameters
  // -------------------------------------------------------------------------

  inline double get_t_ref() const
  {
    return P_.t_ref;
  }

  inline void set_t_ref(const double __v)
  {
    P_.t_ref = __v;
  }

  inline double get_g_Na() const
  {
    return P_.g_Na;
  }

  inline void set_g_Na(const double __v)
  {
    P_.g_Na = __v;
  }

  inline double get_g_K() const
  {
    return P_.g_K;
  }

  inline void set_g_K(const double __v)
  {
    P_.g_K = __v;
  }

  inline double get_g_L() const
  {
    return P_.g_L;
  }

  inline void set_g_L(const double __v)
  {
    P_.g_L = __v;
  }

  inline double get_C_m() const
  {
    return P_.C_m;
  }

  inline void set_C_m(const double __v)
  {
    P_.C_m = __v;
  }

  inline double get_E_Na() const
  {
    return P_.E_Na;
  }

  inline void set_E_Na(const double __v)
  {
    P_.E_Na = __v;
  }

  inline double get_E_K() const
  {
    return P_.E_K;
  }

  inline void set_E_K(const double __v)
  {
    P_.E_K = __v;
  }

  inline double get_E_L() const
  {
    return P_.E_L;
  }

  inline void set_E_L(const double __v)
  {
    P_.E_L = __v;
  }

  inline double get_tau_syn_ex() const
  {
    return P_.tau_syn_ex;
  }

  inline void set_tau_syn_ex(const double __v)
  {
    P_.tau_syn_ex = __v;
  }

  inline double get_tau_syn_in() const
  {
    return P_.tau_syn_in;
  }

  inline void set_tau_syn_in(const double __v)
  {
    P_.tau_syn_in = __v;
  }

  inline double get_g_Ca() const
  {
    return P_.g_Ca;
  }

  inline void set_g_Ca(const double __v)
  {
    P_.g_Ca = __v;
  }

  inline double get_Ca_env() const
  {
    return P_.Ca_env;
  }

  inline void set_Ca_env(const double __v)
  {
    P_.Ca_env = __v;
  }

  inline double get_Ca_i_eq() const
  {
    return P_.Ca_i_eq;
  }

  inline void set_Ca_i_eq(const double __v)
  {
    P_.Ca_i_eq = __v;
  }

  inline double get_tau_Ca() const
  {
    return P_.tau_Ca;
  }

  inline void set_tau_Ca(const double __v)
  {
    P_.tau_Ca = __v;
  }

  inline double get_V_hmCa() const
  {
    return P_.V_hmCa;
  }

  inline void set_V_hmCa(const double __v)
  {
    P_.V_hmCa = __v;
  }

  inline double get_k_mCa() const
  {
    return P_.k_mCa;
  }

  inline void set_k_mCa(const double __v)
  {
    P_.k_mCa = __v;
  }

  inline double get_tau_mCa() const
  {
    return P_.tau_mCa;
  }

  inline void set_tau_mCa(const double __v)
  {
    P_.tau_mCa = __v;
  }

  inline double get_V_hhCa() const
  {
    return P_.V_hhCa;
  }

  inline void set_V_hhCa(const double __v)
  {
    P_.V_hhCa = __v;
  }

  inline double get_k_hCa() const
  {
    return P_.k_hCa;
  }

  inline void set_k_hCa(const double __v)
  {
    P_.k_hCa = __v;
  }

  inline double get_tau_hCa() const
  {
    return P_.tau_hCa;
  }

  inline void set_tau_hCa(const double __v)
  {
    P_.tau_hCa = __v;
  }

  inline double get_g_AHP() const
  {
    return P_.g_AHP;
  }

  inline void set_g_AHP(const double __v)
  {
    P_.g_AHP = __v;
  }

  inline double get_K_AHP() const
  {
    return P_.K_AHP;
  }

  inline void set_K_AHP(const double __v)
  {
    P_.K_AHP = __v;
  }

  inline double get_b_AHP() const
  {
    return P_.b_AHP;
  }

  inline void set_b_AHP(const double __v)
  {
    P_.b_AHP = __v;
  }

  inline double get_tau_AHP() const
  {
    return P_.tau_AHP;
  }

  inline void set_tau_AHP(const double __v)
  {
    P_.tau_AHP = __v;
  }

  inline double get_E_0() const
  {
    return P_.E_0;
  }

  inline void set_E_0(const double __v)
  {
    P_.E_0 = __v;
  }

  inline double get_Vcell() const
  {
    return P_.Vcell;
  }

  inline void set_Vcell(const double __v)
  {
    P_.Vcell = __v;
  }

  inline double get_I_e() const
  {
    return P_.I_e;
  }

  inline void set_I_e(const double __v)
  {
    P_.I_e = __v;
  }


  // -------------------------------------------------------------------------
  //   Getters/setters for internals
  // -------------------------------------------------------------------------

  inline long get_RefractoryCounts() const
  {
    return V_.RefractoryCounts;
  }

  inline void set_RefractoryCounts(const long __v)
  {
    V_.RefractoryCounts = __v;
  }

  inline double get___h() const
  {
    return V_.__h;
  }

  inline void set___h(const double __v)
  {
    V_.__h = __v;
  }

  inline double get_charge() const
  {
    return V_.charge;
  }

  inline void set_charge(const double __v)
  {
    V_.charge = __v;
  }

  inline double get_avogadro() const
  {
    return V_.avogadro;
  }

  inline void set_avogadro(const double __v)
  {
    V_.avogadro = __v;
  }

  inline double get_c2c() const
  {
    return V_.c2c;
  }

  inline void set_c2c(const double __v)
  {
    V_.c2c = __v;
  }

  inline double get___P__I_syn_ex__X__spikeExc__I_syn_ex__X__spikeExc() const
  {
    return V_.__P__I_syn_ex__X__spikeExc__I_syn_ex__X__spikeExc;
  }

  inline void set___P__I_syn_ex__X__spikeExc__I_syn_ex__X__spikeExc(const double __v)
  {
    V_.__P__I_syn_ex__X__spikeExc__I_syn_ex__X__spikeExc = __v;
  }

  inline double get___P__I_syn_ex__X__spikeExc__I_syn_ex__X__spikeExc__d() const
  {
    return V_.__P__I_syn_ex__X__spikeExc__I_syn_ex__X__spikeExc__d;
  }

  inline void set___P__I_syn_ex__X__spikeExc__I_syn_ex__X__spikeExc__d(const double __v)
  {
    V_.__P__I_syn_ex__X__spikeExc__I_syn_ex__X__spikeExc__d = __v;
  }

  inline double get___P__I_syn_ex__X__spikeExc__d__I_syn_ex__X__spikeExc() const
  {
    return V_.__P__I_syn_ex__X__spikeExc__d__I_syn_ex__X__spikeExc;
  }

  inline void set___P__I_syn_ex__X__spikeExc__d__I_syn_ex__X__spikeExc(const double __v)
  {
    V_.__P__I_syn_ex__X__spikeExc__d__I_syn_ex__X__spikeExc = __v;
  }

  inline double get___P__I_syn_ex__X__spikeExc__d__I_syn_ex__X__spikeExc__d() const
  {
    return V_.__P__I_syn_ex__X__spikeExc__d__I_syn_ex__X__spikeExc__d;
  }

  inline void set___P__I_syn_ex__X__spikeExc__d__I_syn_ex__X__spikeExc__d(const double __v)
  {
    V_.__P__I_syn_ex__X__spikeExc__d__I_syn_ex__X__spikeExc__d = __v;
  }

  inline double get___P__I_syn_in__X__spikeInh__I_syn_in__X__spikeInh() const
  {
    return V_.__P__I_syn_in__X__spikeInh__I_syn_in__X__spikeInh;
  }

  inline void set___P__I_syn_in__X__spikeInh__I_syn_in__X__spikeInh(const double __v)
  {
    V_.__P__I_syn_in__X__spikeInh__I_syn_in__X__spikeInh = __v;
  }

  inline double get___P__I_syn_in__X__spikeInh__I_syn_in__X__spikeInh__d() const
  {
    return V_.__P__I_syn_in__X__spikeInh__I_syn_in__X__spikeInh__d;
  }

  inline void set___P__I_syn_in__X__spikeInh__I_syn_in__X__spikeInh__d(const double __v)
  {
    V_.__P__I_syn_in__X__spikeInh__I_syn_in__X__spikeInh__d = __v;
  }

  inline double get___P__I_syn_in__X__spikeInh__d__I_syn_in__X__spikeInh() const
  {
    return V_.__P__I_syn_in__X__spikeInh__d__I_syn_in__X__spikeInh;
  }

  inline void set___P__I_syn_in__X__spikeInh__d__I_syn_in__X__spikeInh(const double __v)
  {
    V_.__P__I_syn_in__X__spikeInh__d__I_syn_in__X__spikeInh = __v;
  }

  inline double get___P__I_syn_in__X__spikeInh__d__I_syn_in__X__spikeInh__d() const
  {
    return V_.__P__I_syn_in__X__spikeInh__d__I_syn_in__X__spikeInh__d;
  }

  inline void set___P__I_syn_in__X__spikeInh__d__I_syn_in__X__spikeInh__d(const double __v)
  {
    V_.__P__I_syn_in__X__spikeInh__d__I_syn_in__X__spikeInh__d = __v;
  }


  // -------------------------------------------------------------------------
  //   Getters/setters for inline expressions
  // -------------------------------------------------------------------------
  
  inline double get_m_infAHP() const
  {
    return pow((S_.ode_state[State_::Ca_i] / P_.K_AHP), 2) / (pow((S_.ode_state[State_::Ca_i] / P_.K_AHP), 2) + P_.b_AHP);
  }

  inline double get_m_infCa() const
  {
    return y_shape(S_.ode_state[State_::V_m], P_.V_hmCa, P_.k_mCa);
  }

  inline double get_h_infCa() const
  {
    return y_shape(S_.ode_state[State_::V_m], P_.V_hhCa, (-P_.k_hCa));
  }

  inline double get_E_Ca() const
  {
    return 2 * P_.E_0 * std::log(P_.Ca_env / S_.ode_state[State_::Ca_i]);
  }

  inline double get_I_syn_exc() const
  {
    return S_.ode_state[State_::I_syn_ex__X__spikeExc];
  }

  inline double get_I_syn_inh() const
  {
    return S_.ode_state[State_::I_syn_in__X__spikeInh];
  }

  inline double get_I_Na() const
  {
    return P_.g_Na * S_.ode_state[State_::Act_m] * S_.ode_state[State_::Act_m] * S_.ode_state[State_::Act_m] * S_.ode_state[State_::Inact_h] * (S_.ode_state[State_::V_m] - P_.E_Na);
  }

  inline double get_I_K() const
  {
    return (P_.g_K * S_.ode_state[State_::Act_n] * S_.ode_state[State_::Act_n] * S_.ode_state[State_::Act_n] * S_.ode_state[State_::Act_n] + P_.g_AHP * pow(S_.ode_state[State_::m_AHP], 2)) * (S_.ode_state[State_::V_m] - P_.E_K);
  }

  inline double get_I_Ca() const
  {
    return (-P_.g_Ca) * S_.ode_state[State_::m_Ca] * S_.ode_state[State_::h_Ca] * (S_.ode_state[State_::V_m] - (2 * P_.E_0 * std::log(P_.Ca_env / S_.ode_state[State_::Ca_i])));
  }

  inline double get_I_L() const
  {
    return P_.g_L * (S_.ode_state[State_::V_m] - P_.E_L);
  }

  inline double get_alpha_n() const
  {
    return (0.01 * (S_.ode_state[State_::V_m] / 1.0 + 55.0)) / (1.0 - std::exp((-(S_.ode_state[State_::V_m] / 1.0 + 55.0)) / 10.0));
  }

  inline double get_beta_n() const
  {
    return 0.125 * std::exp((-(S_.ode_state[State_::V_m] / 1.0 + 65.0)) / 80.0);
  }

  inline double get_alpha_m() const
  {
    return (0.1 * (S_.ode_state[State_::V_m] / 1.0 + 40.0)) / (1.0 - std::exp((-(S_.ode_state[State_::V_m] / 1.0 + 40.0)) / 10.0));
  }

  inline double get_beta_m() const
  {
    return 4.0 * std::exp((-(S_.ode_state[State_::V_m] / 1.0 + 65.0)) / 18.0);
  }

  inline double get_alpha_h() const
  {
    return 0.07 * std::exp((-(S_.ode_state[State_::V_m] / 1.0 + 65.0)) / 20.0);
  }

  inline double get_beta_h() const
  {
    return 1.0 / (1.0 + std::exp((-(S_.ode_state[State_::V_m] / 1.0 + 35.0)) / 10.0));
  }


  // -------------------------------------------------------------------------
  //   Getters/setters for input buffers
  // -------------------------------------------------------------------------
  
  inline nest::RingBuffer& get_spikeInh() {return B_.get_spikeInh();};
  inline nest::RingBuffer& get_spikeExc() {return B_.get_spikeExc();};
  inline nest::RingBuffer& get_I_stim() {return B_.get_I_stim();};
  // -------------------------------------------------------------------------
  //   Function declarations
  // -------------------------------------------------------------------------


  //
  double y_shape(double, double, double) const
  ;

  // -------------------------------------------------------------------------
  //   Member variables of neuron model.
  //   Each model neuron should have precisely the following four data members,
  //   which are one instance each of the parameters, state, buffers and variables
  //   structures. Experience indicates that the state and variables member should
  //   be next to each other to achieve good efficiency (caching).
  //   Note: Devices require one additional data member, an instance of the 
  //   ``Device`` child class they belong to.
  // -------------------------------------------------------------------------

  Parameters_ P_;  //!< Free parameters.
  State_      S_;  //!< Dynamic state.
  Variables_  V_;  //!< Internal Variables
  Buffers_    B_;  //!< Buffers.

  //! Mapping of recordables names to access functions
  static nest::RecordablesMap<hhca_psc_alpha> recordablesMap_;
  friend int hhca_psc_alpha_dynamics( double, const double y[], double f[], void* pnode );

}; /* neuron hhca_psc_alpha */

inline nest::port hhca_psc_alpha::send_test_event(nest::Node& target, nest::rport receptor_type, nest::synindex, bool)
{
  // You should usually not change the code in this function.
  // It confirms that the target of connection @c c accepts @c nest::SpikeEvent on
  // the given @c receptor_type.
  nest::SpikeEvent e;
  e.set_sender(*this);
  return target.handles_test_event(e, receptor_type);
}

inline nest::port hhca_psc_alpha::handles_test_event(nest::SpikeEvent&, nest::port receptor_type)
{
    // You should usually not change the code in this function.
    // It confirms to the connection management system that we are able
    // to handle @c SpikeEvent on port 0. You need to extend the function
    // if you want to differentiate between input ports.
    if (receptor_type != 0)
    {
      throw nest::UnknownReceptorType(receptor_type, get_name());
    }
    return 0;
}

inline nest::port hhca_psc_alpha::handles_test_event(nest::CurrentEvent&, nest::port receptor_type)
{
  // You should usually not change the code in this function.
  // It confirms to the connection management system that we are able
  // to handle @c CurrentEvent on port 0. You need to extend the function
  // if you want to differentiate between input ports.
  if (receptor_type != 0)
  {
    throw nest::UnknownReceptorType(receptor_type, get_name());
  }
  return 0;
}

inline nest::port hhca_psc_alpha::handles_test_event(nest::DataLoggingRequest& dlr, nest::port receptor_type)
{
  // You should usually not change the code in this function.
  // It confirms to the connection management system that we are able
  // to handle @c DataLoggingRequest on port 0.
  // The function also tells the built-in UniversalDataLogger that this node
  // is recorded from and that it thus needs to collect data during simulation.
  if (receptor_type != 0)
  {
    throw nest::UnknownReceptorType(receptor_type, get_name());
  }

  return B_.logger_.connect_logging_device(dlr, recordablesMap_);
}

inline void hhca_psc_alpha::get_status(DictionaryDatum &__d) const
{
  // parameters
  def<double>(__d, "t_ref", get_t_ref());
  def<double>(__d, "g_Na", get_g_Na());
  def<double>(__d, "g_K", get_g_K());
  def<double>(__d, "g_L", get_g_L());
  def<double>(__d, "C_m", get_C_m());
  def<double>(__d, "E_Na", get_E_Na());
  def<double>(__d, "E_K", get_E_K());
  def<double>(__d, "E_L", get_E_L());
  def<double>(__d, "tau_syn_ex", get_tau_syn_ex());
  def<double>(__d, "tau_syn_in", get_tau_syn_in());
  def<double>(__d, "g_Ca", get_g_Ca());
  def<double>(__d, "Ca_env", get_Ca_env());
  def<double>(__d, "Ca_i_eq", get_Ca_i_eq());
  def<double>(__d, "tau_Ca", get_tau_Ca());
  def<double>(__d, "V_hmCa", get_V_hmCa());
  def<double>(__d, "k_mCa", get_k_mCa());
  def<double>(__d, "tau_mCa", get_tau_mCa());
  def<double>(__d, "V_hhCa", get_V_hhCa());
  def<double>(__d, "k_hCa", get_k_hCa());
  def<double>(__d, "tau_hCa", get_tau_hCa());
  def<double>(__d, "g_AHP", get_g_AHP());
  def<double>(__d, "K_AHP", get_K_AHP());
  def<double>(__d, "b_AHP", get_b_AHP());
  def<double>(__d, "tau_AHP", get_tau_AHP());
  def<double>(__d, "E_0", get_E_0());
  def<double>(__d, "Vcell", get_Vcell());
  def<double>(__d, "I_e", get_I_e());

  // initial values for state variables in ODE or kernel
  def<long>(__d, "r", get_r());
  def<double>(__d, "V_m", get_V_m());
  def<double>(__d, "Ca_i", get_Ca_i());
  def<double>(__d, "alpha_n_init", get_alpha_n_init());
  def<double>(__d, "beta_n_init", get_beta_n_init());
  def<double>(__d, "alpha_m_init", get_alpha_m_init());
  def<double>(__d, "beta_m_init", get_beta_m_init());
  def<double>(__d, "alpha_h_init", get_alpha_h_init());
  def<double>(__d, "beta_h_init", get_beta_h_init());
  def<double>(__d, "Act_m", get_Act_m());
  def<double>(__d, "Inact_h", get_Inact_h());
  def<double>(__d, "Act_n", get_Act_n());
  def<double>(__d, "h_Ca", get_h_Ca());
  def<double>(__d, "m_Ca", get_m_Ca());
  def<double>(__d, "m_AHP", get_m_AHP());
  def<double>(__d, "I_syn_ex__X__spikeExc", get_I_syn_ex__X__spikeExc());
  def<double>(__d, "I_syn_ex__X__spikeExc__d", get_I_syn_ex__X__spikeExc__d());
  def<double>(__d, "I_syn_in__X__spikeInh", get_I_syn_in__X__spikeInh());
  def<double>(__d, "I_syn_in__X__spikeInh__d", get_I_syn_in__X__spikeInh__d());

  ArchivingNode::get_status( __d );

  (*__d)[nest::names::recordables] = recordablesMap_.get_list();
  def< double >(__d, nest::names::gsl_error_tol, P_.__gsl_error_tol);
  if ( P_.__gsl_error_tol <= 0. ){
    throw nest::BadProperty( "The gsl_error_tol must be strictly positive." );
  }
}

inline void hhca_psc_alpha::set_status(const DictionaryDatum &__d)
{
  // parameters
  double tmp_t_ref = get_t_ref();
  updateValue<double>(__d, "t_ref", tmp_t_ref);
  double tmp_g_Na = get_g_Na();
  updateValue<double>(__d, "g_Na", tmp_g_Na);
  double tmp_g_K = get_g_K();
  updateValue<double>(__d, "g_K", tmp_g_K);
  double tmp_g_L = get_g_L();
  updateValue<double>(__d, "g_L", tmp_g_L);
  double tmp_C_m = get_C_m();
  updateValue<double>(__d, "C_m", tmp_C_m);
  double tmp_E_Na = get_E_Na();
  updateValue<double>(__d, "E_Na", tmp_E_Na);
  double tmp_E_K = get_E_K();
  updateValue<double>(__d, "E_K", tmp_E_K);
  double tmp_E_L = get_E_L();
  updateValue<double>(__d, "E_L", tmp_E_L);
  double tmp_tau_syn_ex = get_tau_syn_ex();
  updateValue<double>(__d, "tau_syn_ex", tmp_tau_syn_ex);
  double tmp_tau_syn_in = get_tau_syn_in();
  updateValue<double>(__d, "tau_syn_in", tmp_tau_syn_in);
  double tmp_g_Ca = get_g_Ca();
  updateValue<double>(__d, "g_Ca", tmp_g_Ca);
  double tmp_Ca_env = get_Ca_env();
  updateValue<double>(__d, "Ca_env", tmp_Ca_env);
  double tmp_Ca_i_eq = get_Ca_i_eq();
  updateValue<double>(__d, "Ca_i_eq", tmp_Ca_i_eq);
  double tmp_tau_Ca = get_tau_Ca();
  updateValue<double>(__d, "tau_Ca", tmp_tau_Ca);
  double tmp_V_hmCa = get_V_hmCa();
  updateValue<double>(__d, "V_hmCa", tmp_V_hmCa);
  double tmp_k_mCa = get_k_mCa();
  updateValue<double>(__d, "k_mCa", tmp_k_mCa);
  double tmp_tau_mCa = get_tau_mCa();
  updateValue<double>(__d, "tau_mCa", tmp_tau_mCa);
  double tmp_V_hhCa = get_V_hhCa();
  updateValue<double>(__d, "V_hhCa", tmp_V_hhCa);
  double tmp_k_hCa = get_k_hCa();
  updateValue<double>(__d, "k_hCa", tmp_k_hCa);
  double tmp_tau_hCa = get_tau_hCa();
  updateValue<double>(__d, "tau_hCa", tmp_tau_hCa);
  double tmp_g_AHP = get_g_AHP();
  updateValue<double>(__d, "g_AHP", tmp_g_AHP);
  double tmp_K_AHP = get_K_AHP();
  updateValue<double>(__d, "K_AHP", tmp_K_AHP);
  double tmp_b_AHP = get_b_AHP();
  updateValue<double>(__d, "b_AHP", tmp_b_AHP);
  double tmp_tau_AHP = get_tau_AHP();
  updateValue<double>(__d, "tau_AHP", tmp_tau_AHP);
  double tmp_E_0 = get_E_0();
  updateValue<double>(__d, "E_0", tmp_E_0);
  double tmp_Vcell = get_Vcell();
  updateValue<double>(__d, "Vcell", tmp_Vcell);
  double tmp_I_e = get_I_e();
  updateValue<double>(__d, "I_e", tmp_I_e);

  // initial values for state variables in ODE or kernel
  long tmp_r = get_r();
  updateValue<long>(__d, "r", tmp_r);
  double tmp_V_m = get_V_m();
  updateValue<double>(__d, "V_m", tmp_V_m);
  double tmp_Ca_i = get_Ca_i();
  updateValue<double>(__d, "Ca_i", tmp_Ca_i);
  double tmp_alpha_n_init = get_alpha_n_init();
  updateValue<double>(__d, "alpha_n_init", tmp_alpha_n_init);
  double tmp_beta_n_init = get_beta_n_init();
  updateValue<double>(__d, "beta_n_init", tmp_beta_n_init);
  double tmp_alpha_m_init = get_alpha_m_init();
  updateValue<double>(__d, "alpha_m_init", tmp_alpha_m_init);
  double tmp_beta_m_init = get_beta_m_init();
  updateValue<double>(__d, "beta_m_init", tmp_beta_m_init);
  double tmp_alpha_h_init = get_alpha_h_init();
  updateValue<double>(__d, "alpha_h_init", tmp_alpha_h_init);
  double tmp_beta_h_init = get_beta_h_init();
  updateValue<double>(__d, "beta_h_init", tmp_beta_h_init);
  double tmp_Act_m = get_Act_m();
  updateValue<double>(__d, "Act_m", tmp_Act_m);
  double tmp_Inact_h = get_Inact_h();
  updateValue<double>(__d, "Inact_h", tmp_Inact_h);
  double tmp_Act_n = get_Act_n();
  updateValue<double>(__d, "Act_n", tmp_Act_n);
  double tmp_h_Ca = get_h_Ca();
  updateValue<double>(__d, "h_Ca", tmp_h_Ca);
  double tmp_m_Ca = get_m_Ca();
  updateValue<double>(__d, "m_Ca", tmp_m_Ca);
  double tmp_m_AHP = get_m_AHP();
  updateValue<double>(__d, "m_AHP", tmp_m_AHP);
  double tmp_I_syn_ex__X__spikeExc = get_I_syn_ex__X__spikeExc();
  updateValue<double>(__d, "I_syn_ex__X__spikeExc", tmp_I_syn_ex__X__spikeExc);
  double tmp_I_syn_ex__X__spikeExc__d = get_I_syn_ex__X__spikeExc__d();
  updateValue<double>(__d, "I_syn_ex__X__spikeExc__d", tmp_I_syn_ex__X__spikeExc__d);
  double tmp_I_syn_in__X__spikeInh = get_I_syn_in__X__spikeInh();
  updateValue<double>(__d, "I_syn_in__X__spikeInh", tmp_I_syn_in__X__spikeInh);
  double tmp_I_syn_in__X__spikeInh__d = get_I_syn_in__X__spikeInh__d();
  updateValue<double>(__d, "I_syn_in__X__spikeInh__d", tmp_I_syn_in__X__spikeInh__d);

  // We now know that (ptmp, stmp) are consistent. We do not
  // write them back to (P_, S_) before we are also sure that
  // the properties to be set in the parent class are internally
  // consistent.
  ArchivingNode::set_status(__d);

  // if we get here, temporaries contain consistent set of properties
  set_t_ref(tmp_t_ref);
  set_g_Na(tmp_g_Na);
  set_g_K(tmp_g_K);
  set_g_L(tmp_g_L);
  set_C_m(tmp_C_m);
  set_E_Na(tmp_E_Na);
  set_E_K(tmp_E_K);
  set_E_L(tmp_E_L);
  set_tau_syn_ex(tmp_tau_syn_ex);
  set_tau_syn_in(tmp_tau_syn_in);
  set_g_Ca(tmp_g_Ca);
  set_Ca_env(tmp_Ca_env);
  set_Ca_i_eq(tmp_Ca_i_eq);
  set_tau_Ca(tmp_tau_Ca);
  set_V_hmCa(tmp_V_hmCa);
  set_k_mCa(tmp_k_mCa);
  set_tau_mCa(tmp_tau_mCa);
  set_V_hhCa(tmp_V_hhCa);
  set_k_hCa(tmp_k_hCa);
  set_tau_hCa(tmp_tau_hCa);
  set_g_AHP(tmp_g_AHP);
  set_K_AHP(tmp_K_AHP);
  set_b_AHP(tmp_b_AHP);
  set_tau_AHP(tmp_tau_AHP);
  set_E_0(tmp_E_0);
  set_Vcell(tmp_Vcell);
  set_I_e(tmp_I_e);
  set_r(tmp_r);
  set_V_m(tmp_V_m);
  set_Ca_i(tmp_Ca_i);
  set_alpha_n_init(tmp_alpha_n_init);
  set_beta_n_init(tmp_beta_n_init);
  set_alpha_m_init(tmp_alpha_m_init);
  set_beta_m_init(tmp_beta_m_init);
  set_alpha_h_init(tmp_alpha_h_init);
  set_beta_h_init(tmp_beta_h_init);
  set_Act_m(tmp_Act_m);
  set_Inact_h(tmp_Inact_h);
  set_Act_n(tmp_Act_n);
  set_h_Ca(tmp_h_Ca);
  set_m_Ca(tmp_m_Ca);
  set_m_AHP(tmp_m_AHP);
  set_I_syn_ex__X__spikeExc(tmp_I_syn_ex__X__spikeExc);
  set_I_syn_ex__X__spikeExc__d(tmp_I_syn_ex__X__spikeExc__d);
  set_I_syn_in__X__spikeInh(tmp_I_syn_in__X__spikeInh);
  set_I_syn_in__X__spikeInh__d(tmp_I_syn_in__X__spikeInh__d);


  updateValue< double >(__d, nest::names::gsl_error_tol, P_.__gsl_error_tol);
  if ( P_.__gsl_error_tol <= 0. )
  {
    throw nest::BadProperty( "The gsl_error_tol must be strictly positive." );
  }
};

#endif /* #ifndef HHCA_PSC_ALPHA */