Retsim: a retinal simulator


Overview

"Retsim" is a set of scripts that construct a simple model of a retinal neuron and its presynaptic circuit. A light stimulus is transduced by an array of photoreceptors (rods and/or cones), and the resulting signals are synaptically transmitted to an array of bipolar cells, which in turn transmit their signals to one or more ganglion cells. The arrays of different neuron types are generated as semi-random arrays, given a cell density and a regularity (mean/s.d) or by array size or number. Synaptic contacts are automatically set based on a connection algorithm specified in a paramater table.

The main script is "retsim1.n" or "retsim.cc" which defines a set of parameters to control the construction of the stimulus, calls construction subroutines to make the different cell arrays, calls connection subroutines to connect the cell arrays, and defines a procedure to display the model, and run an experiment on it. The "makcel" script constructs the neurons, and the "synfuncs" script makes the connections.

Script              Function

retsim              main script, oversees construction, connection, display, and run.
retsim_var          variable definitions, set pointers for command-line values.
makcel              makes arrays of neurons, either realistic or artificial.
synfuncs            connects arrays and individual neurons with synapses based on algorithm.
celseg              makes spheres and cables with biophysical properties.
celfuncs            functions to help make cells.
stimfuncs           stimulus functions.
plot_funcs          plotting functions.
sb_makfuncs         starburst cell make functions.
sb_recfuncs         starburst cell recording functions.
setexpt             sets dynamic loaded experiment files
onplot_movie        movie frame functions
onplot_dsgc_movie   DS ganglion cell movie functions (includes "onplot_movie")
maknval             script to create table of parameters "nval.n".   
expt_gc_cbp_flash   experiment file: defines and sets parameters, sets up and runs experiment.
nval.n              parameter table
dens.n              channel density table
Retsim is organized around a table of parameters "nval.n". Each column contains the parameters for one cell type, and each row gives the values of one parameter for all the cell types. The table can be viewed and modified using a standard text editor, which makes it easy to change/copy values. The "nval.n" table sets the default values for the construction of the neural circuit. It is created by "maknval" which allows different versions of nval.n to be created. The parameter values in "nval.n" can of course be overridden in any particular experiment, or from the command line.

Some of the parameters describe the neural morphology and how a neuron is to be constructed, and others describe the array geometry. Still others describe the membrane biophysics and synaptic connections. A separate table ("dens.n") contains the channel types and densities for each region of the neuron.

Definining extent of neural arrays

Retsim starts by constructing the largest and most downstream neuron. Typically this is a ganglion cell, but it can also be another neuron such as an amacrine cell (e.g. the starburst amacrine) or a bipolar or horizontal cell. The size of this cell determines how large the model and its arrays of presynaptic cells will be. Next, the presynaptic array is generated, given a cell density and regularity (mean/s.d.). Typically the regularity is set to 5-10, but very realistic arrays can be generated with regularities from 3 (almost random), to 50 (triangular/hexagonal array as in foveal cones). The extent of this array determines the size of the next higher presynaptic array (typically photoreceptors). Which neuron types are included in the simulation are defined by the user in the "experiment file".

If a smaller array is required, a larger cell and array of presynaptic neurons can be trimmed using the "arrsiz" parameter which sets the maximum size in microns for the array. Any neurons outside this size are trimmed away before the simulation is run.

Generating cell morphologies

To construct a cell with realistic morphology, several algorithms are available:

1) A cell can be digitized from a photograph or a confocal stack of images. The information is placed into a text file, organized by points, called "nodes", that describe the locations of soma, axon, and dendrites and their branch-points. Each node connects with a cable to its "parent" node, and this information is listed in the file, along with the cable diameter, (x,y,z) location, and the "region", used for supplying biophysical properties:

-----------------------start of morphology file---------------------
#
#  beta cell anatomy file,  2006 Oct 4
#
#    node     parent   dia      xbio     ybio    zbio   region      dendr
#
       0        0       21        0        0      -25   SOMA        0
       1        0     2.17       21    -2.47       -8   PROX        1
       2        1    0.833     22.8   0.0287       -5   DEND        1
       3        2     1.33     23.3    0.776        0   DEND        1
       4        0      0.5     13.9    -6.32        0   DEND        2
       5        0      1.5    -3.25    -33.6      -30   HILLOCK     0
       6        5      0.5    -6.81    -37.6      -30   AXON_THIN   0
       7        6      0.5    -25.5    -66.4      -30   AXON_THIN   0
       8        7      0.5    -121.2  -160.2      -30   AXON        0

There are several methods available for digitizing neural morphologies:

Digizing neurons with Xfig

You can digitize manually using a photograph or drawing imported into "xfig" (illustrator program in Linux). You digitize a cell's morphology to label its nodes with numbers (see Constructing Neural Circuits->Digitizing cell morphologies"). You manually enter the labels for each node. Xfig will then remember their (x,y) positions. Save the .fig file, convert to Neuron-C format (above) using "fig2nc", then use a text editor to correct the parent nodes and add the diameters. You can find fig2nc in "nc/models/retsim/runconf".

Digizing neurons with IJ-MorphDig

IJ-MorphDig is a Java plugin for ImageJ, an image-processor program. You can import a stack of confocal images, then place node points, along with their diameters measured with a "caliper tool". MorphDig outputs the data in the Neuron-C standard morphology format (above). You can find the IJ-MorphDig package in "nc/models/retsim/runconf".

Digizing neurons with NeuroLucida

You can convert neural morphologies from NeuroLucida format using "dsconv2", which is a script in bash/gawk for Linux. You can find this script in "nc/models/ds/ds3".

Artificial morphology from parameters

You can generate an artificial morphology by algorithm (in "retsim/makcel.cc"), based on the number of primary dendrites, dendritic field extent, and branching pattern. Quite realistic morphologies can be generated for horizontal cells, some types of amacrine and ganglion cells.

Artificial morphology from connection algorithm

An artificial morphology can be generated by the connection algorithm. A "skeleton" of the cell consisting of the soma and proximal dendrites is created at the appropriate location (see "Making arrays of neurons" above) and the dendrites or axon (where applicable) are extended to the closest branch of the nearest neuron of the other type. For example, photoreceptors connect to horizontal cells by extending the horizontal cell dendrites. This algorithm is performed sequentially over all the photoreceptors, at random using a Gaussian probability profile, so that relatively random branches are generated for the first several photoreceptors, which generates a relatively realistic branching pattern.

Array topology from connection algorithm

To generate an array of neurons, you specify the morphology type, either from a digitized file, or from an artificial morphology type. You can also specify the size and rotation of the cell. Normally the rotation is random so that you can use a digitized morphology without concern that its dendrites will generate a regular array of dendritic overlaps. The density of the array can be specified either using the overall array size in microns along with a number of cells, or by defining the cell density (cells/mm2).

Connecting neurons

After the presynaptic arrays of neurons are generated, they are synaptically connected with an algorithm that for each presynaptic neuron connects it to a nearby postsynaptic neuron(s), and extends the presynaptic axon or the postsynaptic dendritic tree, if appropriate, to create a realistic anatomy. Realistic numbers of connections are generated, so that a postsynaptic neuron can receive synaptic connections from several presynaptic neurons, with multiple contacts if appropriate, and a presynaptic neuron can connect to several postsynaptic neurons if appropriate. For example, a bipolar cell receives synaptic contacts from several photoreceptors, but the nearest ones are most likely to make contacts.

One connection algorithm available in retsim calculates a Gaussian probability for making connections, so that each connection is made at random, yet the closest presynaptic neurons are more likely to connect. This allows two random arrays of neurons to be connected with Gaussian weighting functions relatively smoothly. The exact number of connections between a presynaptic and a postsynaptic cell can vary, but the average is closely controlled.

Trimming the arrays

After the neural arrays are generated and interconnected according to a connection algorithm, the neurons that didn't get connected are removed. This is necessary because the presynaptic circuit of a ganglion cell only extends typically 20-50 um beyond the ganglion cell's dendritic tips, yet precisely which presynaptic neurons are connected depends on the connection algorithm, not the original placement of the neurons in the presynaptic arrays.

During the synaptic connection procedure, for each neuron the numbers of presynaptic and postsynaptic neurons it connects to are totalized and saved in a table. Any neurons that don't connect to a postsynaptic cell are removed. The process is started by first checking the layer immediately presynaptic to the ganglion cell, i.e. the bipolar cells, and afterwards checking the more distal neurons, i.e. the photoreceptors.

Stimulus types

Stimuli can include voltage clamp, current clamp, and a variety of light stimuli such as spots, bars, sine wave gratings, etc. When the stimulus procedure is called, the stimulus is added to a list that is run at the correct time during the simulation (i.e. during the "step()" procedure). To generate complex stimuli, for example a bar moving to/fro, a set of stimulus functions is available in "stimfuncs".

Record types

A variety of recording methods are available. An experiment can define a list of nodes to record from, either current or voltage. Light flux into a photoreceptor can be recorded, as well as the concentration of calcium or a neurotransmitter or second messenger. The recorded values can be stored into an array or file or plotted onto the video screen.

Several plotting functions are available to generate plots. Plots can include several traces, e.g. several recording points, and several of these plots can be displayed simultaneously on the screen. Each trace can have its own color and a label and units. Plotting functions are available for "voltage at a node", "synaptic release rate", or for displaying the voltage or some other parameter of a neuron as a color applied to the display of morphology.

Onplot procedure

An "onplot" procedure can be defined which runs automatically at plot time, i.e. when the plot traces are updated, controlled by the "ploti" variable. Although this procedure is not necessary (because the simulation can be run incrementally), it allows the simulation to run faster. For example, the onplot procedure allows a special procedure to compute some function of the neural circuit's responses. It also can be used to plot more complex functions, such as the color-coded voltage superimposed on the morphology. For the compiled version, the onplot procedure is defined by the "setonplot()" procedure.

Making movies

To make a movie, include "onplot_movie" in your experiment file, and call it at plot time, i.e. inside the "onplot()" procedure. At each plot step, it displays the voltage (or other parameter such as inactivation) of the membrane as a color code superimposed on the morphology.

You can look at the movie using the "vid" display, or you can generate separate frames using the "-P name" command line switch. This creates an individual PostScript file for each frame, which you can convert to another appropriate file format. Typically you will need to convert all the frames to a ppm format, then use "mpeg_encode" to create a movie.

You create the movie with a shell script like this:

#! /bin/tcsh -f
#
# make_movie script
#
ps2ppm100 $argv*.ps
movconvert -f "$argv"_ -n 2000
cp xxx_paramfile2 "$argv"_paramfile2
replace xxx "$argv" "$argv"_paramfile2
mpeg_encode "$argv"_paramfile2
rm "$argv"*.ppm

Since the vid display integrates the graphics input from the simulator, the frames it generates after the first do not contain static objects (calibration bars, etc) that should remain throughout the movie. The "movconvert" program accomplishes this integration in the movie for the frames in .ppm format. These are then read and converted to a standard movie format by "mpeg_encode". To view the movie in you can use "mplayer".

Compiling and linking the "C++ compiled" version

The NeuronC API is defined in "nc/src/ncfuncs.h" and described in the NeuronC User's Manual. Each function call in "ncfuncs.h" replaces a statement in the interpreted version of NeuronC. To see an example of source code for this compiled version, see the "retsim.cc" model in nc/models/retsim", described in Retsim: a retinal simulator. The command-line switches for "retsim" are identical to those listed by "nc -h", but note that running "retsim" without arguments displays a list of simulation experiments and parameters. In addition simulation variables can be set from the command line.

The NeuronC API is incorporated into a static library called "libnc.a". This file is created in "nc/src" and must be linked with a neural circuit program such as "nc/models/retsim/retsim.cc". To link this library correctly you may need to set the "NC_HOME" variable in nc/models/retsim/makefile, either by setting it as an environment variable or by changing the makefile. The libnc.a library is created as a static library because as a dynamic library it reduces run-time speed.

The experiments defined for "nc/models/retsim" such as "expt_gc_cbp_flash.cc" are arranged to be dynamically linked to the "retsim" program at runtime, i.e. each experiment is compiled into a dynamic library, e.g. "expt_gc_cbp_flash.so". To allow this process of dynamic runtime linking, you must set the environment variable LD_LIBRARY_PATH or include the directory where the experiment ".so" file is located in (for Linux) "/etc/ld.so.conf". That will allow the runtime linker to find "expt_gc_cbp_flash.so"

(tcsh)  setenv LD_LIBRARY_PATH .
(bash)  export LD_LIBRARY_PATH="."

At the top of the "retsim.cc" script, there are several files included:

 #include "ncio.h"         // defines "ncfprintf()" for I/O to a C++ stream
 #include "ncfuncs.h"      // defines the C++ functions for the simulator
 #include "retcolors.h"    // defines the standard colors 0-15  
 #include "retsim.h"       // defines constants and functions for retsim.cc
 #include "retsim_var.cc"  // defines parameters and "setptrs()" which initializes them
 #include "ncinit.h"       // defines a null "setptrs()" in case it is not already defined
 #include "setexpt.h"      // defines "defparams()", "setparams()", and "runexpt()" for the experiment file

Parameter initialization

Note that a call to "setptr()" or "setptrn()" is necessary for each variable that you will set from the command line (or from the GUI window that runs the simulator). The reason for this is to find and set the address of a parameter for the simulator's run-time initialization. The "setptr()" function initializes the variable to its "undefined" value, so that later the "notinit()" function can tell whether the variable has been initialized -- this is useful to decide whether a default value should be applied. The "setptrn()" function is exactly the same as "setptr()" except that it does not initialize the parameter.

Defining an experiment

The retsim script is controlled by a set of variables that tell it which neurons to create and how to connect them. The variables are read from the default neuron parameter table, "nval.n", but are also set by an "experiment file", and from the command line.

Typically an experiment file defines the general form of the experiment. It directs retsim which cells to include in the model, what synaptic connections and biophysical properties they should have. Then the retsim script constructs the neural circuit model, and returns control back to the experiment file, which runs the experiment. The retsim script, either the interpreted ("retsim1.n") or compiled ("retsim.cc") version, is designed to be run with many different experiments. It is typically run with several script files which define the details of how to construct all the neurons and how to connect them. Many experiment files can be created to define different circuits or different experiment protocols.

Usually the experiment file allows parameters to change the details, i.e. the exact details of the neurons or their spacing, the size, frequency, or timing of a stimulus, or the exact form of the output plots. The idea is to minimize the number of different experiment files by selecting appropriate parameters to control their behavior.

An interpreted experiment file should be written in the nc script language, and should contain "setparams()" and "runexpt()" procedures, as explained below. The setparams() procedure sets the values of parameters, for example to control how the circuit is constructed. The "runexpt()" procedure sets up stimuli and plots, and runs the experiment using a "step" or "run" statement. A good way to start would be to take an existing experiment, e.g. "expt_gc_cbp_flash.n", and modify it to make a different circuit and/or experiment.

A compiled experiment file should be written as a standard .cc file, then compiled and linked as a dynamically-linked library (in linux, ".so"). This allows the retsim script to select an experiment file at runtime instead of requiring that retsim be linked separately with each experiment file. A good way to accomplish this is to look in the "makefile" and modify an existing entry for compiling and linking an experment file.

The experiment file typically contains 3 procedures which are automatically run by the "retsim" script (see "setexpt.cc"):

defparams()   Defines the experiment and its parameters for command line.
              Called before "setvar()" sets variables from command line.

setparams()   Sets variables controlling construction of the circuit.
              Called after defparams but before construction.

runexpt()     Runs the experiment. Fine tunes neurons, sets stimuli and plots.
              Called after construction.
The "runexpt()" procedure can include a "for" statement to run an experiment iteratively. Typically, a stimulus is generated, and the "step()" procedure is called to run the simulation forward in time. Then the loop runs the stimulus again, possibly with a small variation, e.g. a different clamp voltage for a voltage clamp, and the step procedure is run again.
// The defparams procedure defines parameters needed by the experiment.
//  It is run before "setvar()" (which defines the parameters from the command line). 

void defparams(void)
{
  setptr("temp_freq", &temp_freq);
  setptr("ntrials",   &ntrials);
  setptr("dstim",     &dstim);
  setptr("sdia",      &sdia);
  setptr("stimtime",  &stimtime);
  setptr("minten",    &minten);
  setptr("scontrast", &scontrast);
}

// The setparams procedure sets parameters needed for construction of the circuit.

void setparams(void)
{
  make_rods = 0;
  make_cones= 1;        /* make cones, cbp, gc */
  make_ha   = 0;
  make_hb   = 0;
  make_cbp  = 1;
  make_gc   = 1;
  if (notinit(bg_inten)) bg_inten = 2.0e4;      /* background light intensity */
}

// The runexpt() procedure sets up the experiment stimuli and plots, and
//  runs the experiment using "step()" or "run()".  

void runexpt(void)
{
   double temp_freq;

  if (notinit(stimtime))   stimtime = .10;       /* stimulus time */
  if (notinit(minten))       minten = bg_inten;  /* background intensity (for make cone)*/
  if (notinit(scontrast)) scontrast = .5;        /* intensity increment */
  if (notinit(temp_freq)) temp_freq = 2;

  dtrial = 1 / temp_freq;
  exptdur = dtrial * ntrials;

  plot_v_nod(ct=xcone,cn=midcone,n=soma,Vmin=-.037,Vmax =-.027,colr=cyan,"", -1, -1); /* plot Vcones*/
  stim_spot(sdia, 0, 0, minten*scontrast, start=t+stimtime,dur=dstim);
  step(dtrial);
}

Examples of how to run retsim

   nc     --expt gc_cbp_flash --ninfo 2 -d 1 -v retsim1.n | vid                       display the model
   retsim --expt gc_cbp_flash --ninfo 2 -d 1 -v           | vid 

   nc     --expt gc_cbp_flash -d 1 -R retsim1.n > file.pov                            display into povray format
   retsim --expt gc_cbp_flash -d 1 -R           > file.pov

   nc     --expt gc_cbp_flash --mxrot 90 -d 1 -v retsim1.n | vid                      rotate, display the model
   retsim --expt gc_cbp_flash --mxrot 90 -d 1 -v           | vid 

   nc     --expt gc_cbp_flash --ninfo 2 --arrsiz 100 -d 1 -v retsim1.n | vid          limit model to 100 um
   retsim --expt gc_cbp_flash --ninfo 2 --arrsiz 100 -d 1 -v           | vid 

   nc     --expt gc_cbp_flash --ninfo 2 --n_cbp 1 --n_gc 0 -d 1 -v retsim1.n | vid    limit model to 1 bipolar
   retsim --expt gc_cbp_flash --ninfo 2 --n_cbp 1 --n_gc 0 -d 1 -v           | vid 

   nc     --expt gc_cbp_flash --ninfo 2 -v retsim1.n | vid                            run model, display plots
   retsim --expt gc_cbp_flash --ninfo 2 -v | vid 

   nc     --expt gc_cbp_flash retsim1.n > file.r                                      run model into data file
   retsim --expt gc_cbp_flash           > file.r 

   plotmod file.r | vid                                                                display plots from data file