/*
 *  test_mini_brunel_ps.sli
 *
 *  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/>.
 *
 */

 /* BeginDocumentation
Name: testsuite::test_mini_brunel_ps - Test parallel simulation of small Brunel-style network

Synopsis: nest_indirect test_mini_brunel_ps.sli -> -

Description:
   Simulates scaled-down Brunel net with precise timing for different numbers of MPI
   processes and compares results.

Author:  May 2012, Plesser, based on brunel_ps.sli

See: brunel_ps.sli
*/

/unittest (9726) require
/unittest using

/brunel_setup {
/brunel << >> def

brunel begin
/order 50 def     % scales size of network (total 5*order neurons)

/g      250.0 def    % rel strength, inhibitory synapses
/eta    2.0 def    % nu_ext / nu_thresh

/simtime 200.0 def % simulation time [ms]
/dt        0.1 def % simulation step length [ms]

% Number of POSIX threads per program instance.
% When using MPI, the mpirun call determines the number
% of MPI processes (=program instances). The total number
% of virtual processes is #MPI processes x local_num_threads.
/total_num_virtual_procs 4 def

% Compute the maximum of postsynaptic potential
% for a synaptic input current of unit amplitude
% (1 pA)
/ComputePSPnorm
{
  % calculate the normalization factor for the PSP
  (
  a = tauMem / tauSyn;
  b = 1.0 / tauSyn - 1.0 / tauMem;
  % time of maximum
  t_max = 1.0/b * (-LambertWm1(-exp(-1.0/a)/a)-1.0/a);
  % maximum of PSP for current of unit amplitude
  exp(1.0)/(tauSyn*CMem*b) * ((exp(-t_max/tauMem) - exp(-t_max/tauSyn)) / b - t_max*exp(-t_max/tauSyn))
  ) ExecMath
}
def

%%% PREPARATION SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

/NE 4 order mul cvi def  % number of excitatory neurons
/NI 1 order mul cvi def  % number of inhibitory neurons
/N  NI NE add def        % total number of neurons

/epsilon 0.1 def            % connectivity
/CE epsilon NE mul cvi def  % number of excitatory synapses on neuron
/CI epsilon NI mul cvi def  % number of inhibitory synapses on neuron
/C  CE CI add def           % total number of internal synapses per n.
/Cext CE def                % number of external synapses on neuron

/tauMem 20.0 def    % neuron membrane time constant [ms]
/CMem  250.0 def    % membrane capacity [pF]
/tauSyn  0.5 def    % synaptic time constant [ms]
/tauRef  2.0 def    % refractory time [ms]
/U0      0.0 def    % resting potential [mV]
/theta  20.0 def    % threshold


% amplitude of PSP given 1pA current
ComputePSPnorm /J_max_unit Set

% synaptic weights, scaled for our alpha functions, such that
% for constant membrane potential, the peak amplitude of the PSP
% equals J

/delay   1.5 def         % synaptic delay, all connections [ms]
/J       0.1 def         % synaptic weight [mV]
/JE J J_max_unit div def % synaptic weight [pA]
/JI g JE mul neg def     % inhibitory

% threshold rate, equivalent rate of events needed to
% have mean input current equal to threshold
/nu_thresh ((theta * CMem) / (JE*CE*exp(1)*tauMem*tauSyn)) ExecMath def
/nu_ext eta nu_thresh mul def     % external rate per synapse
/p_rate nu_ext Cext mul 1000. mul def % external input rate per neuron
                                        % must be given in Hz

% number of neurons to record from
/Nrec 20 def

end
}
def % brunel_setup

%%% CONSTRUCTION SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


[1 2 4]
{
  brunel_setup
  /brunel using
    % set resolution and total/local number of threads
    0
    <<
      /resolution  dt
      /total_num_virtual_procs total_num_virtual_procs
    >> SetStatus

    /E_net /subnet Create def            % create subnet
    E_net ChangeSubnet                   % enter subnet
    /iaf_psc_alpha_canon NE Create       % create neurons in subnet
    ;                                    % pop gids returned by Create
    0 ChangeSubnet                       % return to full network

    /I_net /subnet Create def            % create subnet
    I_net ChangeSubnet                   % enter subnet
    /iaf_psc_alpha_canon NI Create       % create neurons in subnet
    ;                                    % pop gids returned by Create
    0 ChangeSubnet                       % return to full network

    /expoisson /poisson_generator_ps Create def
    expoisson
    <<                % set firing rate
    /rate p_rate
    >> SetStatus

    /inpoisson /poisson_generator_ps Create def
    inpoisson
    <<
    /rate  p_rate
    >> SetStatus

    /exsd /spike_detector Create def
    exsd
    <<
    /withtime true   % record time of spikes
    /withgid true    % record which neuron spiked
    /precise_times true
    /time_in_steps true    
    >> SetStatus

    /E_neurons E_net GetGlobalNodes def
    /I_neurons I_net GetGlobalNodes def
    /allNeurons E_neurons I_neurons join def

    allNeurons
    {
      <<
      /tau_m   tauMem
      /C_m     CMem
      /tau_syn tauSyn
      /t_ref   tauRef
      /E_L     U0
      /V_th    theta
      /V_m     U0
      /V_reset U0
      /C_m     1.0     % capacitance is unity in Brunel model
      >> SetStatus
    } forall

    /static_synapse << /delay delay >> SetDefaults
    /static_synapse /syn_ex << /weight JE >> CopyModel
    /static_synapse /syn_in << /weight JI >> CopyModel
    
    E_neurons
    {
      /target Set
      expoisson target /syn_ex Connect
      E_neurons target CE /syn_ex RandomConvergentConnect
      I_neurons target CI /syn_in RandomConvergentConnect
    } bind % bind improves efficiency
    forall

    I_neurons
    {
      /target Set
      inpoisson target /syn_ex Connect
      E_neurons target CE /syn_ex RandomConvergentConnect
      I_neurons target CI /syn_in RandomConvergentConnect
    } bind forall
    
    E_neurons Nrec Take
    {
      exsd Connect
    } bind forall
    
    simtime Simulate

    % get events, replace vectors with SLI arrays    
    /ev exsd /events get def
    ev keys { /k Set ev dup k get cva k exch put } forall
    ev 
  endusing

} distributed_process_invariant_events_assert_or_die