ACnet23 primary auditory cortex model (Beeman et al 2019)

 Download zip file 
Help downloading and running models
Accession:261423
These scripts were used to model a patch of layer 2/3 primary auditory cortex, making use of the the improvements to PGENESIS by Crone, et al. (2019). This single layer model contains a 48 x 48 grid of pyramidal cells (PCs) and a 24 x 24 grid of basket cells (BCs). The reduced PC models have 17 compartments with dimensions and passive properties that were fit to human cortical PC reconstructions. This parallel version of the simulation was used by Beeman, et al. (2019) to understand the effects of inhibition of PCs by BCs on auditory evoked potentials.
Reference:
1 . Beeman D, Yu A, Crone J (2019) Studying evoked potentials in large cortical networks with PGENESIS 2.4 BMC Neuroscience 20 Suppl 1:P46
2 . Crone JC, Vindiola MM, Yu AB, Boothe DL, Beeman D, Oie KS, Franaszczuk PJ (2019) Enabling Large-Scale Simulations With the GENESIS Neuronal Simulator Frontiers in Neuroinformatics 13:69
Citations  Citation Browser
Model Information (Click on a link to find other models with that property)
Model Type: Realistic Network;
Brain Region(s)/Organism: Auditory cortex;
Cell Type(s): Neocortex L2/3 pyramidal GLU cell; Neocortex fast spiking (FS) interneuron;
Channel(s):
Gap Junctions:
Receptor(s): GabaA; AMPA;
Gene(s):
Transmitter(s): Gaba; Glutamate;
Simulation Environment: PGENESIS; GENESIS;
Model Concept(s):
Implementer(s): Beeman, Dave;
Search NeuronDB for information about:  Neocortex L2/3 pyramidal GLU cell; GabaA; AMPA; Gaba; Glutamate;
//genesis

// /**********************************************************************
// ** This simulation script and the files included in this package
// ** are Copyright (C) 2013 by David Beeman (dbeeman@colorado.edu)
// ** and are made available under the terms of the
// ** GNU Lesser General Public License version 2.1
// ** See the file copying.txt for the full notice.
// *********************************************************************/

// /*============================================================================

//  Network setup function definitions for the "par_ACnet2" series of simulations

//  ============================================================================*/
/* These functions assume that network has been divided horizontal
   slices that are on nodes 0 through  n_slices-1.  Each slice has
   cells_per_slice = NX*NY / n_slices cells.
*/
function cell_node(cell_num, cells_per_slice) // return the node that cell_num is in
    int cell_num, cells_per_slice
    return {trunc {cell_num/cells_per_slice}} + {worker0}
end
// return the cell index within that node
function cell_index(cell_num, cells_per_slice)
    int cell_num, node, cells_per_slice
    node = {cell_node {cell_num} {cells_per_slice}}
    return {cell_num - (node - worker0)*cells_per_slice}
end

// /***** functions to connect network *****/ 
// see function connect_cells below

/***** functions to set weights and delays *****/

function set_weights(weight)
    float weight
    str Ex_layer = "Ex_" @ {default_layer}
    str Inh_layer = "Inh_" @ {default_layer}
    syn_weight = weight  // set the global variable to the new weight
    // In these simulations, use_weight_decay = 0
    if (!use_weight_decay) // use fixed weights
        // use fixed weights - the default
        rvolumeweight /{Ex_layer}/{Ex_cell_name}[]/soma/spike \
            /{Ex_layer}/{Ex_cell_name}[]/{Ex_ex_synpath} \
              -fixed {syn_weight}
        rvolumeweight /{Ex_layer}/{Ex_cell_name}[]/soma/spike \
            /{Inh_layer}/{Inh_cell_name}[]/{Inh_ex_synpath} \
              -fixed {syn_weight}
        rvolumeweight /{Inh_layer}/{Inh_cell_name}[]/soma/spike  \
            /{Ex_layer}/{Ex_cell_name}[]/{Ex_inh_synpath} \
              -fixed {syn_weight}
        rvolumeweight /{Inh_layer}/{Inh_cell_name}[]/soma/spike  \
            /{Inh_layer}/{Inh_cell_name}[]/{Inh_inh_synpath} \
              -fixed {syn_weight}

        echo "All maxium synaptic weights set to "{weight}
        /* Newer versions of this script use the test:
             if(use_Ex_ex_FACDEP && rand_weight)
        with rand_weight = 0, so that the following randomization of
        Ex_ex weights is not performed. The rand_weight option was intended
        for use with stdp_rules for modeling LTP and LTD. Here the weights
        are randomized, to reproduce the published simulation results. Thie
        has a small, but observable effect on the recovery from the N! peak.
        */
        // Make an exception for Ex_cell excitatory (Ex_ex) weights
        if(use_Ex_ex_FACDEP)
          int n, i
          /* reseed the RNG to give the same weights
             This is necessary only with the option "rand_weight = 1"
             to randomly initialize the Ex_ex weights
          */
          randseed {seed} // Better to use reseed_RNG {seed}
          for (n=0; n < Ex_cells_per_slice; n=n+1)
            str chanpath =  "/" @ {Ex_layer} @"/" \
             @ {Ex_cell_name} @  "[" @ {n} @ "]/" @ {Ex_ex_synpath}
            for (i=0; i <  {getfield {chanpath} nsynapses}; i=i+1)
              if (rand_weight == 1)
                  weight = {rand 0 {2*avg_weight}}
              end
              setfield {chanpath} synapse[{i}].weight {weight}
            end // for (i=0
          end // for (n=0
          if (debug_level)
         echo@{control_node} "All Ex_ex synaptic weights set to average of "{avg_weight}
         echo@{control_node} "All other synaptic weights set to "{syn_weight}
           end
         end // if(use_Ex_ex_FACDEP && rand_weight)
    end //   if (!use_weight_decay)
end


/* If the delay is zero, set it as the fixed delay, else use the conduction
   velocity to calculate delays based on radial distance to the target.
   For a fixed delay, either planardelay or volumedelay can be used.
   As axonal conduction velocities are the same in both the vertical
   direction, and in the horizontal plane, volumedelay should be
   used to account for the vertical distance traveled to the dendrites.
*/

function set_delays(delay)
    float delay
    str Ex_layer = "Ex_" @ {default_layer}
    str Inh_layer = "Inh_" @ {default_layer}
    prop_delay = delay
    if (delay == 0.0)
        rvolumerdelay /{Ex_layer}/{Ex_cell_name}[]/soma/spike -fixed {delay}
        rvolumedelay /{Inh_layer}/{Inh_cell_name}[]/soma/spike -fixed {delay}
    else
        rvolumedelay /{Ex_layer}/{Ex_cell_name}[]/soma/spike -radial {1/delay}
        rvolumedelay /{Inh_layer}/{Inh_cell_name}[]/soma/spike -radial {1/delay}
    end
    if (debug_level)
        echo@{control_node}  "All propagation delays set to "{delay}" sec/m"
    end
end

//=============================================================
//    Functions to set up the network
//=============================================================

function make_prototypes
  /* Step 1: Assemble the components to build the prototype cell under the
     neutral element /library.  This is done by including "prododefs.g"
     before using the function make_prototypes.
  */
  
  /* Step 2: Create the prototype cell specified in 'cellfile', using readcell.
     This should set up the apropriate synchans in specified compartments,
     with a spikegen element "spike" attached to the soma.  This will be
     done in /library, where it will be available to be copied into a network

     In this case there are two types of cells "Ex_cell", "Inh_cell",

  */

  readcell {Ex_cellfile} /library/{Ex_cell_name}
  readcell {Inh_cellfile} /library/{Inh_cell_name}
  addfield /library/{Ex_cell_name}/soma io_index // for ordering i/o messages
  addfield /library/{Inh_cell_name}/soma io_index // for ordering i/o messages

  // In this case, use different values from the defaults in 'cellfile'
  setfield /library/{Ex_cell_name}/{Ex_ex_synpath} gmax {Ex_ex_gmax}
  setfield /library/{Ex_cell_name}/{Ex_inh_synpath} gmax {Ex_inh_gmax}
  setfield /library/{Ex_cell_name}/{Ex_drive_synpath} gmax {Ex_ex_gmax}
  setfield /library/{Ex_cell_name}/soma/spike thresh 0 abs_refract 0.002 \
	output_amp 1
  // Note: the Ex-cell has wider and slower spikes, so use larger abs_refract

  setfield /library/{Inh_cell_name}/{Inh_ex_synpath} gmax {Inh_ex_gmax}
  setfield /library/{Inh_cell_name}/{Inh_inh_synpath} gmax {Inh_inh_gmax}
  setfield /library/{Inh_cell_name}/{Inh_drive_synpath} gmax {Inh_ex_gmax}
  setfield /library/{Inh_cell_name}/soma/spike thresh 0  abs_refract 0.001 \
	 output_amp 1
end

/***** functions to set up the network *****/

/* Setting up hsolve for a network requires setting up a solver for
   one cell of each type in the network and then duplicating the
   solvers.  The procedure is described in the advanced tutorial
   'Simulations with GENESIS using hsolve by Hugo Cornelis' from
   genesis-sim.org/GENESIS/UGTD/Tutorials/advanced-tutorials
*/
function make_hsolve(layer)
    if(!hflag)
        return
    end
    str layer
    // do for Ex_layer
    str netlayer = "/Ex_" @ {layer}
    str cellname = {netlayer} @ "/" @ {Ex_cell_name}
    pushe {cellname}[0]
    create hsolve {solvepath}
    setmethod solver 11 // Use Crank-Nicholson
    setfield {solvepath} chanmode {hsolve_chanmode} \
        path "../[][TYPE=compartment]"
    setfield {solvepath} computeIm 1
    call {solvepath} SETUP
    int i
    for (i = 1 ; i < Ex_cells_per_slice ; i = i + 1)
        call {solvepath} DUPLICATE \
          {cellname}[{i}]/{solvepath}  ../##[][TYPE=compartment]
        setfield {cellname}[{i}]/{solvepath} \
            x {getfield {cellname}[{i}] x} \
            y {getfield {cellname}[{i}] y} \
            z {getfield {cellname}[{i}] z}
    end
    pope
    str netlayer = "/Inh_" @ {layer}
    str cellname = {netlayer} @ "/" @ {Inh_cell_name}
    pushe {cellname}[0]
    create hsolve {solvepath}
    setmethod {solvepath} 11 // Crank-Nicholson
    setfield {solvepath} chanmode  {hsolve_chanmode} path "../[][TYPE=compartment]"
    call {solvepath} SETUP
    int i
    for (i = 1 ; i < Inh_cells_per_slice ; i = i + 1)
        call {solvepath} DUPLICATE \
            {cellname}[{i}]/{solvepath}  ../##[][TYPE=compartment]
        setfield {cellname}[{i}]/{solvepath} \
            x {getfield {cellname}[{i}] x} \
            y {getfield {cellname}[{i}] y} \
            z {getfield {cellname}[{i}] z}
    end
    pope
    if(pdebug)
        echo@{control_node} "Set up hsolve on node " {mynode}
    end
end

function make_network_slice(layer)
    /*
       For the unsliced network, there will be NX cells along the
       x-direction, separated by SEP_X, and NY cells along the y-direction,
       separated by SEP_Y.  The default origin is (0, 0).  This will be the
       coordinates of cell[0].  The last cell, cell[{NX*NY-1}], will be at
       (NX*SEP_X -1, NY*SEP_Y-1).

       For the parallel version, each worker node makes a slice of the net
       with the origin set at the lower left corner of the slice, and a
       soma io_index that represents the cell number on the unsliced net.
       Each slice node will have a set of cells cell[0] -
       cell[{cells_per_slice - 1}], but the soma io_index will be in the
       appropriate part of the range from 0 - NX*NX-1, in order to
       identify its location on the full net.

       Here, each network layer is divided into horizontal slices of dimension
       NX x NY/n_slices.
    */
    int i, slice
    slice = {mynode} - {worker0}  // The slice numbers start with 0
    str layer
    str Ex_layer = "Ex_" @ {layer}
    str Inh_layer = "Inh_" @ {layer}

    createmap /library/{Ex_cell_name} /{Ex_layer} {Ex_NX} {Ex_NY/n_slices} \
      -delta {Ex_SEP_X} {Ex_SEP_Y} \
      -origin 0 {slice * Ex_SEP_Y * Ex_NY / n_slices}
        // Label the cell soma with the io_index. For horizontal slices,
        // this is equal to cell_num on the unsliced network.
        for (i = 0; i < Ex_cells_per_slice; i=i+1)
            setfield  /{Ex_layer}/{Ex_cell_name}[{i}]/soma \
                     io_index {slice * Ex_cells_per_slice + i}
        end


    // Displace the /Inh_layer4 origin to be in between Ex_cells
    createmap /library/{Inh_cell_name} /{Inh_layer} {Inh_NX} {Inh_NY/n_slices} \
      -delta {Inh_SEP_X} {Inh_SEP_Y} \
      -origin  {Ex_SEP_X/2}  {Ex_SEP_Y/2 + slice * Inh_SEP_Y * Inh_NY / n_slices}
        // Label the cell soma with the io_index. For horizontal slices,
        // this is equal to cell_num on the unsliced network.
        for (i = 0; i < Inh_cells_per_slice; i=i+1)
            setfield  /{Inh_layer}/{Inh_cell_name}[{i}]/soma \
                     io_index {slice * Inh_cells_per_slice + i}
        end
end // function make_network_slice

/***** functions to connect network *****/
/* Use function planarconnect_probdecay when volumeconnect3 is not available */

/* planarconnect_probdecay is a specialized version of planarconnect where
   the connection probability is weighted with a probability
   prob0*exp(-r*r/(sigma*sigma)) where r is the radial distance
   between source and destination in the x-y plane.

   Unlike the more general version, it is assumed that all cells in the
   source network are used as sources.  The destination region is defined
   as a ring between a radius rmin and rmax in the destination network.
   The sources are all assumed to be spikegen objects, and the destinations
   to be synchans, or variants such as facsynchans.
   
   An example of the ith cell in the source network would be:

   {src_cell_path}[{i}]{src_spikepath}

   with
   src_cell_path = /Ex_layer/{Ex_cell_name}
   src_spikepath = "soma/spike"

   An example destination would use:

   dst_cell_path = /Inh_layer/{Inh_cell_name}
   dst_synpath = {Inh_ex_synpath}

   For example:

   planarconnect_probdecay /Ex_layer4/{Ex_cell_name} "soma/spike" {Ex_NX*Ex_NY} \
      /Inh_layer4/{Inh_cell_name} {Inh_ex_synpath} {Inh_NX*Inh_NY} \
      0.0 {pyr_range} {Ex_Inh_prob} {Ex_sigma}

  For the parallel version, n_src is the number of cells in a slice,
  Ex_cells_per_slice or Inh_cells_per_slice. But, n_dstis the total number
  of destination cells across all slices, Ex_NX*Ex_NY or Inh_NX*Inh_NY.
*/

function planarconnect_probdecay(src_cell_path, src_spikepath, n_src, \
    dst_cell_path, dst_synpath, n_dst, rmin, rmax, prob0, sigma)

    str src, src_cell_path, src_spikepath, dst, dst_cell_path, dst_synpath
    int n_src, n_dst, i, j, dst_node, dst_index
    float rmin, rmax, prob0, sigma, x, y, dst_x, dst_y, prob, rsqr

    float decay = 1.0/(sigma*sigma)
    float rminsqr = rmin*rmin
    float rmaxsqr = rmax*rmax
    int cells_per_slice = n_dst/n_slices
    /* loop over all source cells - this will be all cells in layer */
    for (i = 0 ; i < {n_src} ; i = i + 1)

       src = {src_cell_path} @ "[" @ {i} @ "]/" @ {src_spikepath}
       x = {getfield {src} x}
       y = {getfield {src} y}

        /* loop over all possible destination cells - all in dest layer */
        for (j = 0 ; j < {n_dst} ; j = j + 1)
	    if (n_dst > Inh_NX*Inh_NY) // The destination is an Ex_layer
                dst_x = (j%Ex_NX)*Ex_SEP_X
                dst_y = ({trunc {j/Ex_NX}})*Ex_SEP_Y
            else // It is an Inh_layer
                dst_x = (j%Inh_NX)*Inh_SEP_X + Ex_SEP_X/2
                dst_y = ({trunc {j/Inh_NX}})*Inh_SEP_Y + Ex_SEP_Y/2
            end
            rsqr = (dst_x - x)*(dst_x - x) + (dst_y - y)*(dst_y - y)
            if( ({rsqr} >= {rminsqr}) && ({rsqr} <= {rmaxsqr}) )
                prob = {prob0}*{exp {-1.0*{rsqr*decay}}}
                if ({rand 0 1} <= prob)
                  // dst_node = {trunc {j/cells_per_slice}} + {worker0}
                  dst_node =  {cell_node {j} {cells_per_slice}}
                  // dst_index = j  - (dst_node - worker0)*cells_per_slice
                  dst_index = {cell_index {j} {cells_per_slice}}
                  dst = {dst_cell_path} @ "[" @ {dst_index} @ "]/" @ {dst_synpath}
                  raddmsg {src} {dst}@{dst_node} SPIKE
                end
            end
        end // dst loop
    end // src loop
end // function planarconnect_probdecay



/* Functions to Connect each source spike generator in src_layer to
   target synchans in dst_layer within the specified range. The
   connection probability is weighted with a probability
   exp(-d*d/(sigma*sigma)) where r is the radial distance between
   source and destination in the x-y plane.

   With the flag use_probdecay_function = 1, the function planarconnect_probdecay
   will be used to make the connections between cells.

   When use_probdecay_function = 0, the built-in command volumeconnect3
   is used with the '-planar' option. This provides faster setup, but a
   rvolumeconnect3 command is not available in PGENESIS.


  Default values used in this simulation:
  float Ex_Ex_prob = 0.15
  float Ex_Inh_prob = 0.45
  float Inh_Ex_prob = 0.6
  float Inh_Inh_prob = 0.6  // just a guess

 Typical usage:

  connect_Ex_Inh /Ex_layer4 /Inh_layer4
  connect_Ex_Inh "Ex_"@{default_layer} "Inh_"@{default_layer}  

*/


function connect_Ex_Ex(src_layer,dst_layer)
  str src_layer, dst_layer
  float Ex_Ex_prob = 0.15
  float Ex_Inh_prob = 0.45
  float Inh_Ex_prob = 0.6
  float Inh_Inh_prob = 0.6  // just a guess
  // distance at which number of targets cells becomes very small
  float pyr_range = 25*Ex_SEP_X 
  float bask_range = 25*Ex_SEP_X

  // Ex_layer cells connect to excitatory synchans
  float Ex_sigma = 10.0*Ex_SEP_X // values from K Yaun SfN 2008 poster fit
  float Inh_sigma = 10.0*Ex_SEP_X

  planarconnect_probdecay /{src_layer}/{Ex_cell_name} "soma/spike" \
      {Ex_cells_per_slice} \
      /{dst_layer}/{Ex_cell_name}  {Ex_ex_synpath} {Ex_NX*Ex_NY} \
      0.0 {pyr_range} {Ex_Ex_prob} {Ex_sigma}
end


function connect_Ex_Inh(src_layer,dst_layer)
  str src_layer,dst_layer
  float Ex_Ex_prob = 0.15
  float Ex_Inh_prob = 0.45
  float Inh_Ex_prob = 0.6
  float Inh_Inh_prob = 0.6  // just a guess
  // distance at which number of targets cells becomes very small
  float pyr_range = 25*Ex_SEP_X 
  float bask_range = 25*Ex_SEP_X

  // Ex_layer cells connect to excitatory synchans
  float Ex_sigma = 10.0*Ex_SEP_X // values from K Yaun SfN 2008 poster fit
  float Inh_sigma = 10.0*Ex_SEP_X

  planarconnect_probdecay /{src_layer}/{Ex_cell_name} "soma/spike" \
      {Ex_cells_per_slice} \
      /{dst_layer}/{Inh_cell_name}  {Inh_ex_synpath} {Inh_NX*Inh_NY} \
      0.0 {pyr_range} {Ex_Inh_prob} {Ex_sigma}
end

function connect_Inh_Ex(src_layer,dst_layer)
  str src_layer,dst_layer
  float Ex_Ex_prob = 0.15
  float Ex_Inh_prob = 0.45
  float Inh_Ex_prob = 0.6
  float Inh_Inh_prob = 0.6  // just a guess
  // distance at which number of targets cells becomes very small
  float pyr_range = 25*Ex_SEP_X 
  float bask_range = 25*Ex_SEP_X

  // Ex_layer cells connect to excitatory synchans
  float Ex_sigma = 10.0*Ex_SEP_X // values from K Yaun SfN 2008 poster fit
  float Inh_sigma = 10.0*Ex_SEP_X

  planarconnect_probdecay /{src_layer}/{Inh_cell_name} "soma/spike" \
      {Inh_cells_per_slice} \
      /{dst_layer}/{Ex_cell_name}  {Ex_inh_synpath} {Ex_NX*Ex_NY} \
      0.0 {bask_range} {Inh_Ex_prob} {Inh_sigma}
end

function connect_Inh_Inh(src_layer,dst_layer)
  str src_layer,dst_layer
  float Ex_Ex_prob = 0.15
  float Ex_Inh_prob = 0.45
  float Inh_Ex_prob = 0.6
  float Inh_Inh_prob = 0.6  // just a guess
  // distance at which number of targets cells becomes very small
  float pyr_range = 25*Ex_SEP_X 
  float bask_range = 25*Ex_SEP_X

  // Ex_layer cells connect to excitatory synchans
  float Ex_sigma = 10.0*Ex_SEP_X // values from K Yaun SfN 2008 poster fit
  float Inh_sigma = 10.0*Ex_SEP_X

  planarconnect_probdecay /{src_layer}/{Inh_cell_name} "soma/spike" \
      {Inh_cells_per_slice} \
      /{dst_layer}/{Inh_cell_name}  {Inh_inh_synpath} {Inh_NX*Inh_NY} \
      0.0 {bask_range} {Inh_Inh_prob} {Inh_sigma}
end

/* function connect_cells is performed by each worker node to make
   connections from its "slice" of the network
*/
function connect_cells
  /* Default values used in this simulation */
  float Ex_Ex_prob = 0.15
  float Ex_Inh_prob = 0.45
  float Inh_Ex_prob = 0.6
  float Inh_Inh_prob = 0.6  // just a guess

  // distance at which number of targets cells becomes very small
  float pyr_range = 25*Ex_SEP_X 
  float bask_range = 25*Ex_SEP_X

  // Ex_layer cells connect to excitatory synchans

    float Ex_sigma = 10.0*Ex_SEP_X // values from K Yaun SfN 2008 poster fit
    float Inh_sigma = 10.0*Ex_SEP_X

    if(use_probdecay_function)
        str src_layer = "Ex_" @ {default_layer}
        str dst_layer = "Ex_" @ {default_layer}
        connect_Ex_Ex {src_layer} {dst_layer}

        str dst_layer = "Inh_" @ {default_layer}
        connect_Ex_Inh  {src_layer} {dst_layer}

        str src_layer = "Inh_" @ {default_layer}
        str dst_layer = "Ex_" @ {default_layer}
        connect_Inh_Ex  {src_layer} {dst_layer}

        str dst_layer = "Inh_" @ {default_layer}
        connect_Inh_Inh  {src_layer} {dst_layer}
    else // only for serial versions with use_probdecay_function = 0
      str src_layer = "Ex_" @ {default_layer}
      str dst_layer = "Ex_" @ {default_layer}
      volumeconnect3 /{src_layer}/{Ex_cell_name}[]/soma/spike \
       /{dst_layer}/{Ex_cell_name}[]/{Ex_ex_synpath} \
		-relative \  // Destination coordinates are measured relative to source
       -sourcemask box -1 -1 -1 1 1 1 \   // Larger than source area ==> all cells
       -destmask ellipsoid 0 0 0 {pyr_range} {pyr_range} {Ex_SEP_Z*0.5}  \
       -desthole box {-Ex_SEP_X*0.5} {-Ex_SEP_Y*0.5} {-Ex_SEP_Z*0.5} \
         {Ex_SEP_X*0.5} {Ex_SEP_Y*0.5} {Ex_SEP_Z*0.5} \
        -probability  {Ex_Ex_prob} -decay {1.0/Ex_sigma} {Ex_Ex_prob} 0.0 2.0 \
       -planar // restrict distance to x-y plane
    str dst_layer = "Inh_" @ {default_layer}
    // Ex-Inh connections don't need an intial desthole
    volumeconnect3 /{src_layer}/{Ex_cell_name}[]/soma/spike \
    /{dst_layer}/{Inh_cell_name}[]/{Inh_ex_synpath} \
    -relative \	    // Destination coordinates are measured relative to source
    -sourcemask box -1 -1 -1  1 1  1 \  // Larger than source area ==> all cells
    -destmask ellipsoid 0 0 0 {pyr_range} {pyr_range}  {Ex_SEP_Z*0.5} \
   -probability {Ex_Inh_prob} -decay {1.0/Ex_sigma} {Ex_Inh_prob} 0.0 2 \
   -planar
   str src_layer = "Inh_" @ {default_layer}
   str dst_layer = "Ex_" @ {default_layer}
    // Inh-Ex connections don't need an intial desthole
    volumeconnect3 /{src_layer}/{Inh_cell_name}[]/soma/spike \
    /{dst_layer}/{Ex_cell_name}[]/{Ex_inh_synpath} \
    -relative \	    // Destination coordinates are measured relative to source
    -sourcemask box -1 -1 -1  1 1  1 \  // Larger than source area ==> all cells
    -destmask ellipsoid 0 0 0  {bask_range} {bask_range}  {Inh_SEP_Z*0.5} \
    -probability {Inh_Ex_prob}  -decay {1.0/Inh_sigma} {Inh_Ex_prob} 0.0 2 \
    -planar // restrict distance to x-y plane

    // This could be optional
    // Inh_layer4 cells connect to inhibitory synchans
    str src_layer = "Inh_" @ {default_layer}
    str dst_layer = "Inh_" @ {default_layer}
    volumeconnect3 {src_layer}/{Inh_cell_name}[]/soma/spike \
    /{dst_layer}/{Inh_cell_name}[]/{Inh_inh_synpath} \
    -relative \	    // Destination coordinates are measured relative to source
    -sourcemask box -1 -1 -1  1 1  1 \   // Larger than source area ==> all cells
    -destmask ellipsoid 0 0 0 {bask_range} {bask_range}  {Inh_SEP_Z*0.5}  \
    -desthole box {-Inh_SEP_X*0.5} {-Inh_SEP_Y*0.5} {-Inh_SEP_Z*0.5} \
       {Inh_SEP_X*0.5} {Inh_SEP_Y*0.5} {Inh_SEP_Z*0.5} \
    -probability {Inh_Inh_prob} -decay {1.0/Inh_sigma} {Inh_Inh_prob} 0.0 2 \
    -planar // restrict distance to x-y plane           
  end // if
end // function connect_cells

// Utility functions to calculate statistics
function print_avg_syn_number // to be executed by control_node
    int n, dst_node, dst_index
    float num_Ex_ex = 0
    float num_Ex_inh = 0
    str Ex_layer = "Ex_" @ {default_layer}
    str Inh_layer = "Inh_" @ {default_layer}
    for (n=0; n < Ex_NX*Ex_NY; n=n+1)
        dst_node = {cell_node {n} {Ex_cells_per_slice}}
        dst_index = {cell_index {n} {Ex_cells_per_slice}}
        num_Ex_ex = num_Ex_ex + {getsyncount@{dst_node} \
            /{Ex_layer}/{Ex_cell_name}[{dst_index}]/{Ex_ex_synpath}}
        num_Ex_inh = num_Ex_inh + {getsyncount@{dst_node} \
            /{Ex_layer}/{Ex_cell_name}[{dst_index}]/{Ex_inh_synpath}}
    end
    num_Ex_ex = num_Ex_ex/(Ex_NX*Ex_NY)
    num_Ex_inh = num_Ex_inh/(Ex_NX*Ex_NY)

    float num_Inh_ex = 0
    float num_Inh_inh = 0
    for (n=0; n < Inh_NX*Inh_NY; n=n+1)
        dst_node = {cell_node {n} {Inh_cells_per_slice}}
        dst_index = {cell_index {n} {Inh_cells_per_slice}}
        num_Inh_ex = num_Inh_ex + {getsyncount@{dst_node} \
            /{Inh_layer}/{Inh_cell_name}[{dst_index}]/{Inh_ex_synpath}}
        num_Inh_inh = num_Inh_inh + {getsyncount@{dst_node} \
            /{Inh_layer}/{Inh_cell_name}[{dst_index}]/{Inh_inh_synpath}}
    end
    num_Inh_ex = num_Inh_ex/(Inh_NX*Inh_NY)
    num_Inh_inh = num_Inh_inh/(Inh_NX*Inh_NY)

    echo "Average number of Ex_ex synapses per cell: " {num_Ex_ex}
    echo "Average number of Ex_inh synapses per cell: " {num_Ex_inh}
    echo "Average number of Inh_ex synapses per cell: " {num_Inh_ex}
    echo "Average number of Inh_inh synapses per cell: " {num_Inh_inh}

end // print_avg_syn_number

function print_avg_input_number
// to be executed by control_node
   int n, dst_node, dst_index, row, min, max
    float num_Ex_drive
    str Ex_layer = "Ex_" @ {default_layer}
    for (row=0; row < Ex_NY; row=row+1)
        num_Ex_drive = 0.0
        min = row*Ex_NX; max = min + Ex_NX
        for (n=min; n<max; n=n+1)
            dst_node = {cell_node {n} {Ex_cells_per_slice}}
            dst_index = {cell_index {n} {Ex_cells_per_slice}}
            num_Ex_drive = num_Ex_drive + {getsyncount@{dst_node} \
	    /{Ex_layer}/{Ex_cell_name}[{dst_index}]/{Ex_drive_synpath}}
	end
        // num_Ex_drive = 1.0*num_Ex_drive
        echo "Row "{row} "Average number of Ex_drive synapses per row: " {num_Ex_drive}
    end // loop over rows
end // function print_avg_input_number