A simple single cell model¶
Building and testing detailed models of individual cells, then optimizing their parameters is usually the first step in building models with multi-compartment cells. Arbor supports a single cell model workflow for this purpose, which is a good way to introduce Arbor’s cell modelling concepts and approach.
Note
Concepts covered in this example:
Intro to building a morphology from a
arbor.segment_tree.
Intro to region and locset expressions.
Intro to decors and cell decorations.
Building a
arbor.cable_cellobject.
Building a
arbor.single_cell_modelobject.
Running a simulation and visualising the results.
The cell¶
The most trivial representation of a cell in Arbor is to model the entire cell as a single cylinder. The following example shows the steps required to construct a model of a cylindrical cell with a length of 6 μm and a radius of 3 μm; Hodgkin–Huxley dynamics and a current clamp stimulus, then run the model for 30 ms.
The first step is to construct the cell. In Arbor, the abstract representation used to
define a cell with branching cable morphology is a
cable_cell, which holds a
description of the cell’s morphology, named regions and locations on the morphology, and
descriptions of ion channels, synapses, threshold detectors and electrical properties.
Our single-segment HH cell has a simple morphology and dynamics, constructed as follows:
import arbor import seaborn # You may have to pip install these. #  and set up a decor object decor = ( arbor.decor() .set_property(Vm=-40) .paint('"soma"', arbor.density("hh")) .place('"midpoint"', arbor.iclamp(10, 2, 0.8), "iclamp") .place('"midpoint"', arbor.threshold_detector(-10), "detector") )
Step (1) constructs a
arbor.segment_tree (see also segment tree).
The segment tree is the representation used to construct the morphology of a cell. A segment is
a tapered cone with a tag; the tag can be used to classify the type of the segment (for example
soma, dendrite etc). To create a segment tree representing our single-cylinder cell, we need to add
one segment to our
tree object. We use the
arbor.segment_tree.append() method, which takes
4 arguments: the parent segment which does not exist for the first segment, so we use
arbor.mnpos;
the proximal
arbor.mpoint (location and radius) of the segment; the distal
arbor.mpoint
of the segment; and the tag.
Step (2) creates a dictionary of labels (
arbor.label_dict). Labels give
names to regions and location described using a DSL
based on s-expressions. Labels from the dictionary can then be used to facilitate adding synapses,
dynamics, stimuli and probes to the cell. We add two labels:
somadefines a region with
(tag 1). Note that this corresponds to the
tagparameter that was used to define the single segment in step (1).
midpointdefines a location at
(location 0 0.5), which is the mid point
0.5of branch
0, which corresponds to the midpoint of the soma on the morphology defined in step (1).
Step (3) constructs a
arbor.decor that describes the distribution and placement
of dynamics and properties on a cell. The cell’s default properties can be modified, and we can use
arbor.decor.paint() and
arbor.decor.place() to further customise it in the
following way:
arbor.decor.set_property()is used to set some default properties on the entire cell. In the above example we set the initial membrane potential to -40 mV.
arbor.decor.paint()is used to set properties or add dynamics to a region of the cell. We call this method ‘painting’ to convey that we are working on sections of a cell, as opposed to precise locations: for example, we might want to paint a density ion channel on all dendrites, and then place a synapse at the tip of the axon. In the above example we paint HH dynamics on the region we previously named
"soma"in our label dictionary.
arbor.decor.place()is used to add objects on a precise
arbor.locationon a cell. Examples of objects that are placed are synapses, threshold detectors, current stimuli, and probes. In the above example we place a current stimulus
arbor.iclampwith a duration of 2 ms and a current of 0.8 nA, starting at 10 ms on the location we previously labelled
"midpoint". We also place a
arbor.threshold_detectorwith a threshold of -10 mV on the same location.
Step (4) constructs the
arbor.cable_cell from the segment tree and dictionary of labelled regions and locations.
The single cell model¶
Once the cell description has been built, the next step is to build and run the simulation.
Arbor provides an interface for constructing single cell models with the
arbor.single_cell_model helper that creates a model from a cell description, with
an interface for recording outputs and running the simulation.
The single cell model has 4 main functions:
It holds the global properties of the model
It registers probes on specific locations on the cell to measure the voltage.
It runs the simulation.
It collects spikes from threshold detectors and voltage traces from registered probes.
Right now, we’ll only set a probe and run the simulation.
# (4) Create cell and the single cell model based on it cell = arbor.cable_cell(tree, labels, decor) # (5) Make single cell model. m = arbor.single_cell_model(cell) # (6) Attach voltage probe sampling at 10 kHz (every 0.1 ms). m.probe("voltage", '"midpoint"', frequency=10)
Step (5) instantiates the
arbor.single_cell_model
with our single-compartment cell.
Step (6) adds a
arbor.single_cell_model.probe()
used to record variables from the model. Three pieces of information are
provided: the type of quantity we want probed (voltage), the location where we want to
probe (‘“midpoint”’), and the frequency at which we want to sample (10 kHz).
Step (7) runs the actual simulation for a duration of 30 ms.
The results¶
Our cell and model have been defined and we have run our simulation. Now we can look at what the threshold detector and a voltage probes from our model have produced.
# (7) Run simulation for 30 ms of simulated activity. m.run(tfinal=30) # (8) Print spike times. if len(m.spikes) > 0: print("{} spikes:".format(len(m.spikes))) for s in m.spikes: print("{:3.3f}".format(s)) else: print("no spikes") # (9) Plot the recorded voltages over time. print("Plotting results ...")
Step (8) accesses
arbor.single_cell_model.spikes()
to print the spike times. A single spike should be generated at around the same time the stimulus
we provided in step (3) gets activated (10ms).
Step (9) plots the measured potentials during the runtime of the simulation. The sampled quantities
can be accessed through
arbor.single_cell_model.traces().
We should be seeing something like this:
Plot of the potential over time for the voltage probe added in step (6).¶
The full code¶
You can find the source code for this example in full at
python/examples/single_cell_model.py.