diff --git a/doc/ai_library.rst b/doc/ai_library.rst
new file mode 100644
index 0000000000000000000000000000000000000000..8ca7bf49f0cc96fb1ee554822daa5327d21e1c7d
--- /dev/null
+++ b/doc/ai_library.rst
@@ -0,0 +1,39 @@
+.. _libref:
+
+Library reference
+#################
+
+Low level library reference material goes here, e.g. `range` library documentation.
+
+Utility wrappers and containers
+--------------------------------
+
+.. cpp:namespace:: arb::util
+
+.. cpp:class:: unique_any
+
+    A container for a single value of any type that is move constructable.
+    Used in the Arbor API where a type of a value passed to or from the API
+    is decided at run time.
+
+.. cpp:class:: any_ptr
+
+   Holds a pointer to an arbitrary type, together with the type information.
+
+   .. cpp:function:: template <typename T> T as()
+
+      Retrieve the pointer as type T. If T is ``void *`` or the same
+      as the type of the pointer stored in ``any_ptr``, return the held
+      value, cast accordingly. Otherwise return ``nullptr``.
+
+   ``any_ptr`` can be used with ``util::any_cast``, so that
+   ``util::any_cast<T>(p)`` is equivalent to ``p.as<T>()`` for a value ``p``
+   of type ``any_ptr``.
+
+.. cpp:function:: template <typename T> any_cast(...)
+
+    Equivalent to ``std::any_cast`` for ``std::any`` arguments, ``any_cast``
+    also performs analagous casting for the :cpp:class:`unique_any` and
+    :cpp:class:`any_ptr` utility classes.
+
+    See :ref:`cppcable_cell`.
diff --git a/doc/nmodl.rst b/doc/ai_nmodl.rst
similarity index 100%
rename from doc/nmodl.rst
rename to doc/ai_nmodl.rst
diff --git a/doc/sampling_api.rst b/doc/ai_sampling_api.rst
similarity index 100%
rename from doc/sampling_api.rst
rename to doc/ai_sampling_api.rst
diff --git a/doc/simd_api.rst b/doc/ai_simd_api.rst
similarity index 100%
rename from doc/simd_api.rst
rename to doc/ai_simd_api.rst
diff --git a/doc/cable_cell.rst b/doc/co_cable_cell.rst
similarity index 71%
rename from doc/cable_cell.rst
rename to doc/co_cable_cell.rst
index db3f2ee2365947ef00c0860429fa7dae9cc27167..4c5d27a35785cc53edad8c6e06e62a369f88dcbe 100644
--- a/doc/cable_cell.rst
+++ b/doc/co_cable_cell.rst
@@ -1,19 +1,22 @@
 .. _cablecell:
 
-Cable Cells
+Cable cells
 ===========
 
 An Arbor *cable cell* is a full description of a cell with morphology and cell
 dynamics, where cell dynamics include ion species and their properties, ion
-channels, synapses, gap junction sites, stimulii and spike detectors.
+channels, synapses, gap junction sites, stimuli and spike detectors.
 Arbor cable cells are constructed from a morphology and a label dictionary,
 and provide a rich interface for specifying the cell's dynamics.
 
 .. note::
-    Before reading this page, it is recommended that you first read about
-    :ref:`morphology descriptions <morph-morphology>`, and also
-    :ref:`label dictionary <labels-dictionary>` that are used to describe
-    :ref:`locations <labels-locset>` and :ref:`regions <labels-region>` on a cell.
+    The cable cell does not have *one* dedicated page, it has a few more! This page describes how to build a full description of a cable cell, based on three components that are broken out into their own pages:
+
+    * :ref:`morphology descriptions <morph-morphology>`;
+    * :ref:`label dictionary <labels-dictionary>` that are used to describe :ref:`locations <labels-locset>` and :ref:`regions <labels-region>` on a cell;
+    * :ref:`mechanisms <mechanisms>`.
+
+    It can be helpful to consult those pages for some of the sections of this page.
 
 .. _cablecell-decoration:
 
@@ -42,19 +45,19 @@ of dynamics in Arbor:
   * :ref:`Synapses <cable-synapses>`.
   * :ref:`Gap junction sites <cable-gj-sites>`.
   * :ref:`Threshold detectors <cable-threshold-detectors>` (spike detectors).
-  * :ref:`Stimulii <cable-stimulii>`.
+  * :ref:`Stimuli <cable-stimuli>`.
   * :ref:`Probes <cable-probes>`.
 
 .. _cablecell-paint:
 
-Painted Dynamics
+Painted dynamics
 ''''''''''''''''
 
 Painted dynamics are applied to a subset of the surface and/or volume of cells.
 They can be specified at three different levels:
 
 * *globally*: a global default for all cells in a model.
-* *per-cell*: overide the global defaults for a specific cell.
+* *per-cell*: override the global defaults for a specific cell.
 * *per-region*: specialize on specific cell regions.
 
 This hierarchical approach for resolving parameters and properties allows
@@ -75,7 +78,7 @@ The types of dynamics, and where they can be defined, are
                   ,       **region**, **cell**, **global**
    cable properties,       ✓, ✓, ✓
    ion initial conditions, ✓, ✓, ✓
-   density mechnism,       ✓, --, --
+   density mechanism,       ✓, --, --
    ion rev pot mechanism,  --, ✓, ✓
    ion valence,            --, --, ✓
 
@@ -123,6 +126,18 @@ for setting cell-wide defaults for properties, and the
     cell.paint('"soma"', Vm=-50, cm=0.01, rL=35)
     cell.paint('"axon"', Vm=-60, rL=40)
 
+.. _cable-discretisation:
+
+Discretisation
+~~~~~~~~~~~~~~~~
+
+For the purpose of simulation, cable cells are decomposed into discrete
+subcomponents called *control volumes* (CVs), following the finite volume method
+terminology. Each control volume comprises a connected subset of the
+morphology. Each fork point in the morphology will be the responsibility of
+a single CV, and as a special case a zero-volume CV can be used to represent
+a single fork point in isolation.
+
 .. _cable-density-mechs:
 
 Density mechanisms
@@ -134,7 +149,7 @@ which describe biophysical processes. These are processes
 that are distributed in space, but whose behaviour is defined purely
 by the state of the cell and the process at any given point.
 
-The most common use for density mecahnisms is to describe ion channel dynamics,
+The most common use for density mechanisms is to describe ion channel dynamics,
 for example the ``hh`` and ``pas`` mechanisms provided by NEURON and Arbor,
 which model classic Hodgkin-Huxley and passive leaky currents respectively.
 
@@ -159,10 +174,10 @@ Take for example a mechanism passive leaky dynamics:
 
 .. code-block:: Python
 
-    # Create pas mechanism with default parameter values (set in NOMDL file).
+    # Create pas mechanism with default parameter values (set in NMODL file).
     m1 = arbor.mechanism('passive')
 
-    # Create default mechainsm with custom conductance (range)
+    # Create default mechanism with custom conductance (range)
     m2 = arbor.mechanism('passive', {'g': 0.1})
 
     # Create a new pas mechanism with that changes reversal potential (global)
@@ -183,7 +198,7 @@ Ion species
 Arbor allows arbitrary ion species to be defined, to extend the default
 calcium, potassium and sodium ion species.
 A ion species is defined globally by its name and valence, which
-can't be overriden at cell or region level.
+can't be overridden at cell or region level.
 
 .. csv-table:: Default ion species in Arbor
    :widths: 15, 10, 10
@@ -202,7 +217,7 @@ Each ion species has the following properties:
 
 Properties 1, 2 and 3 must be defined, and are used as the initial values for
 each quantity at the start of the simulation. They are specified globally,
-then specialised at cell and region level.
+then specialized at cell and region level.
 
 The reversal potential of an ion species is calculated by an
 optional *reversal potential mechanism*.
@@ -251,7 +266,7 @@ The NMODL code for the
 can be used as a guide for how to calculate reversal potentials.
 
 While the reversal potential mechanism must be the same for a whole cell,
-the initial concentrations and reversal potential can be localised for regions
+the initial concentrations and reversal potential can be localized for regions
 using the *paint* interface:
 
 .. code-block:: Python
@@ -267,32 +282,34 @@ using the *paint* interface:
 
 .. _cablecell-place:
 
-Placed Dynamices
+Placed dynamics
 ''''''''''''''''
 
 Placed dynamics are discrete countable items that affect or record the dynamics of a cell,
-and are asigned to specific locations.
+and are assigned to specific locations.
 
 .. _cable-synapses:
 
-Synapses
-~~~~~~~~
+Connection sites
+~~~~~~~~~~~~~~~~
 
-Synapses are instances of NMODL POINT mechanisms.
+Connections (synapses) are instances of NMODL POINT mechanisms. See also :ref:`modelconnections`.
 
 .. _cable-gj-sites:
 
 Gap junction sites
 ~~~~~~~~~~~~~~~~~~
 
+See :ref:`modelgapjunctions`.
+
 .. _cable-threshold-detectors:
 
 Threshold detectors (spike detectors).
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. _cable-stimulii:
+.. _cable-stimuli:
 
-Stimulii
+Stimuli
 ~~~~~~~~
 
 .. _cable-probes:
@@ -300,90 +317,9 @@ Stimulii
 Probes
 ~~~~~~
 
-Python API
-----------
-
-Creating a cable cell
-'''''''''''''''''''''
-
-.. py:class:: cable_cell
-
-    A cable cell is constructed from a :ref:`morphology <morph-morphology>`
-    and an optional :ref:`label dictionary <labels-dictionary>`.
-
-    .. note::
-        The regions and locsets defined in the label dictionary are
-        :ref:`concretised <labels-concretise>` when the cable cell is constructed,
-        and an exception will be thrown if an invalid label expression is found.
-
-        There are two reasons an expression might be invalid:
-
-        1. Explicitly refers to a location of cable that does not exist in the
-           morphology, for example ``(branch 12)`` on a cell with 6 branches.
-        2. Incorrect label reference: circular reference, or a label that does not exist.
-
-
-    .. code-block:: Python
-
-        import arbor
-
-        # Construct the morphology from an SWC file.
-        tree = arbor.load_swc('granule.swc')
-        morph = arbor.morphology(tree, spherical_root=True)
-
-        # Define regions using standard SWC tags
-        labels = arbor.label_dict({'soma': '(tag 1)',
-                                   'axon': '(tag 2)',
-                                   'dend': '(join (tag 3) (tag 4))'})
-
-        # Construct a cable cell.
-        cell = arbor.cable_cell(morph, labels)
-
-    .. method:: set_properties(Vm=None, cm=None, rL=None, tempK=None)
-
-        Set default values of cable properties on the whole cell.
-        Overrides the default global values, and can be overriden by painting
-        the values onto regions.
-
-        :param str region: name of the region.
-        :param Vm: Initial membrane voltage [mV].
-        :type Vm: float or None
-        :param cm: Membrane capacitance [F/m²].
-        :type cm: float or None
-        :param rL: Axial resistivity of cable [Ω·cm].
-        :type rL: float or None
-        :param tempK: Temperature [Kelvin].
-        :type tempK: float or None
-
-        .. code-block:: Python
-
-            # Set cell-wide values for properties
-            cell.set_properties(Vm=-70, cm=0.01, rL=100, tempK=280)
-
-
-    .. method:: paint(region, [Vm=None, cm=None, rL=None, tempK=None])
-
-        Set cable properties on a region.
-
-        :param str region: name of the region.
-        :param Vm: Initial membrane voltage [mV].
-        :type Vm: float or None
-        :param cm: Membrane capacitance [F/m²].
-        :type cm: float or None
-        :param rL: Axial resistivity of cable [Ω·cm].
-        :type rL: float or None
-        :param tempK: Temperature [Kelvin].
-        :type tempK: float or None
-
-        .. code-block:: Python
-
-            # Specialize resistivity on soma
-            cell.paint('"soma"', rL=100)
-            # Specialize resistivity and capacitance on the axon, where
-            # axon is defined using a region expression.
-            cell.paint('(tag 2)', cm=0.05, rL=80)
-
-.. py:class:: ion
+API
+---
 
-    properties of an ionic species.
+* :ref:`Python <pycable_cell>`
+* :ref:`C++ <cppcable_cell>`
 
diff --git a/doc/model_concepts.rst b/doc/co_cell.rst
similarity index 70%
rename from doc/model_concepts.rst
rename to doc/co_cell.rst
index 9a5a0ee953a812a4c065749bf8bf33bbd2e22c05..2fe8503ba2e87f939aa180a22b310bc1fca852ec 100644
--- a/doc/model_concepts.rst
+++ b/doc/co_cell.rst
@@ -1,10 +1,3 @@
-.. _modelconcepts:
-
-Concepts
-########
-
-This section describes some of the core concepts of Arbor.
-
 .. _modelcells:
 
 Cells
@@ -13,9 +6,6 @@ Cells
 The basic unit of abstraction in an Arbor model is a cell.
 A cell represents the smallest model that can be simulated.
 Cells interact with each other via spike exchange and gap junctions.
-Cells can be of various types, admitting different representations and implementations.
-Arbor currently supports specialized leaky integrate and fire cells and cells representing artificial spike sources in
-addition to multi-compartment neurons.
 
 .. table:: Identifiers used to uniquely refer to cells and objects like synapses on cells.
 
@@ -44,21 +34,22 @@ are formed between two **gap junction sites**.
 
 A cell can have multiple sources, targets and gap junction site objects. Each object has a local :gen:`index`
 relative to other objects of the same type on that cell.
-A unique (:gen:`gid`, :gen:`index`) pair defned by a :gen:`cell_member` can be used to uniquely identify
+A unique (:gen:`gid`, :gen:`index`) pair defined by a :gen:`cell_member` can be used to uniquely identify
 objects on a cell in a global model.
 
 
-Cell Kinds
+Cell kinds
 ----------
 
-.. table:: The types of cell supported by Arbor
+.. table:: The 4 types of cell supported by Arbor
 
     ========================  ===========================================================
     Cell Kind                 Description
     ========================  ===========================================================
     **cable**                 Cell with morphology described by branching
-                              1D cable segments.
-    **lif**                   Leaky-integrate and fire neuron.
+                              1D cable segments and user configurable mechanisms.
+    **lif**                   Single-compartment no-mechanism leaky integrate-and-fire
+                              neuron.
     **spiking**               Proxy cell that generates spikes from a user-supplied
                               time sequence.
     **benchmark**             Proxy cell used for benchmarking (developer use only).
@@ -85,6 +76,8 @@ Cell Kinds
    * **Gap Junction Sites**: These refer to the sites of :ref:`gap junctions <modelgapjunctions>`.
      They are declared by specifying a location on a branch of the cell.
 
+   Because cable cells are the main cell kind in Arbor and have more properties than listed here, they have a :ref:`dedicated page <cablecell>`.
+
 2. **LIF Cells**
 
    A single compartment leaky integrate and fire neuron with one **source** and one **target**.
@@ -99,33 +92,10 @@ Cell Kinds
 
    Proxy cell used for benchmarking, and used by developers to benchmark the spike exchange and event delivery infrastructure.
 
-.. _modelconnections:
-
-Connections
-===========
-
-Connections implement chemical synapses between **source** and **target** cells and are characterized by having a transmission delay.
-
-Connections in Arbor are defined in two steps:
-
-1. Create **Source** and **Target** on two cells: a source defined on one cell, and a target defined on another.
-2. Declare the connection in the :ref:`recipe <modelrecipe>`: with a source and target idenfied using :gen:`cell_member`, a connection delay and a connection weight.
-
-.. _modelgapjunctions:
-
-Gap Junctions
-=============
-
-Gap junctions represent electrical synapses where transmission between cells is bidirectional and direct.
-They are modeled as a conductance between two **gap junction sites** on two cells.
-
-Similarly to `Connections`, Gap Junctions in Arbor are defined in two steps:
+Most Arbor users will want to use the cable cell, because it's the only cell kind that supports complex morphologies and user-defined mechanisms. See cable cells :ref:`dedicated page <cablecell>`. The LIF cell can be used to build networks with point-neurons.
 
-1. A **gap junction site** is created on each of the two cells.
-   These locations need to be declared on the :ref:`cell <modelcells>`.
-2. Gap Junction instantiation in the :ref:`recipe <modelrecipe>`: The **gap junction sites** are indexed using :gen:`cell_member`
-   because a single cell may have more than one gap junction site.
-   A gap junction is instantiated by providing two **gap junction sites'** and a conductance in μS.
+API
+---
 
-   .. Note::
-      Only cable cells support gap junctions as of now.
+* :ref:`Python <pycell>`
+* :ref:`C++ <cppcell>`
diff --git a/doc/model_domdec.rst b/doc/co_domdec.rst
similarity index 83%
rename from doc/model_domdec.rst
rename to doc/co_domdec.rst
index 8c7890dd2441be73464e1491d07f5ef130ef6052..a98b1b75116ac0b4337eecbfdcb0e994e92f08d2 100644
--- a/doc/model_domdec.rst
+++ b/doc/co_domdec.rst
@@ -1,6 +1,6 @@
 .. _modeldomdec:
 
-Domain Decomposition
+Domain decomposition
 ====================
 
 A *domain decomposition* describes the distribution of the model over the available computational resources.
@@ -14,11 +14,16 @@ cell group will run on a CPU core or the GPU. The domain decomposition is solely
 of cells across cell groups and domains.
 
 
-Load Balancers
+Load balancers
 --------------
 
 A *load balancer* generates the domain decomposition using the model recipe and a description of the available computational
 resources on which the model will run described by an execution context.
 Currently Arbor provides one load balancer and more will be added over time.
 
-Arbor's Python interface of domain decomposition and load balancers is documented in :ref:`pydomdec` and the C++ interface in :ref:`cppdomdec`.
+API
+---
+
+* :ref:`Python <pydomdec>`
+* :ref:`C++ <cppdomdec>`
+
diff --git a/doc/model_hardware.rst b/doc/co_hardware.rst
similarity index 85%
rename from doc/model_hardware.rst
rename to doc/co_hardware.rst
index 84b01b6a281b0d16e2d1327325a51fa64afd0479..4aea89e622b7a8810d69f5e9bf11bc6893db1e30 100644
--- a/doc/model_hardware.rst
+++ b/doc/co_hardware.rst
@@ -1,7 +1,7 @@
 .. _modelhardware:
 
-Hardware
-========
+Hardware management
+===================
 
 *Local resources* are locally available computational resources, specifically the number of hardware threads and the number of GPUs.
 
@@ -19,9 +19,13 @@ An *allocation* enumerates the computational resources to be used for a simulati
    applications/libraries.
 
 
-Execution Context
+Execution context
 -----------------
 
 An *execution context* contains the local thread pool, and optionally the GPU state and MPI communicator, if available. Users of the library configure contexts, which are passed to Arbor methods and types.
 
-See :ref:`pyhardware` for documentation of the Python interface and :ref:`cpphardware` for the C++ interface for managing hardware resources.
+API
+---
+
+* :ref:`Python <pyhardware>`
+* :ref:`C++ <cpphardware>`
diff --git a/doc/co_interconnectivity.rst b/doc/co_interconnectivity.rst
new file mode 100644
index 0000000000000000000000000000000000000000..168b4660a1e522e2bf9e28ebcf6a560cc2d72254
--- /dev/null
+++ b/doc/co_interconnectivity.rst
@@ -0,0 +1,51 @@
+.. _co_interconnectivity:
+
+Interconnectivity
+#################
+
+Networks can be regarded as a sort of graph, where the nodes are cells and the edges
+describe the communications between them. In Arbor, two sorts of edges are modelled: a
+connection abstracts the propagation of action potentials (spikes) through the network,
+while a gap junction is used to describe a direct electrical connection between two cells.
+Connections only capture the propagation delay and attenuation associated with spike
+connectivity: the biophysical modelling of the chemical synapses themselves is the
+responsibility of the target cell model.
+
+Connection sites and gap junction sites are defined on locations on cells (more on cells :ref:`here <modelcells>`). A recipe lets you define which sites are connected to which.
+
+.. _modelconnections:
+
+Connections
+===========
+
+Connections implement chemical synapses between **source** and **target** cells and are characterized by having a transmission delay.
+
+Connections in Arbor are defined in two steps:
+
+1. Create **Source** and **Target** on two cells: a source defined on one cell, and a target defined on another.
+2. Declare the connection in the :ref:`recipe <modelrecipe>`: with a source and target identified using :gen:`cell_member`, a connection delay and a connection weight.
+
+.. _modelgapjunctions:
+
+Gap junctions
+=============
+
+Gap junctions represent electrical synapses where transmission between cells is bidirectional and direct.
+They are modeled as a conductance between two **gap junction sites** on two cells.
+
+Similarly to `Connections`, Gap Junctions in Arbor are defined in two steps:
+
+1. A **gap junction site** is created on each of the two cells.
+   These locations need to be declared on the :ref:`cell <modelcells>`.
+2. Gap Junction instantiation in the :ref:`recipe <modelrecipe>`: The **gap junction sites** are indexed using :gen:`cell_member`
+   because a single cell may have more than one gap junction site.
+   A gap junction is instantiated by providing two **gap junction sites'** and a conductance in μS.
+
+   .. Note::
+      Only cable cells support gap junctions as of now.
+
+API
+---
+
+* :ref:`Python <pyinterconnectivity>`
+* :ref:`C++ <cppinterconnectivity>`
diff --git a/doc/labels.rst b/doc/co_labels.rst
similarity index 79%
rename from doc/labels.rst
rename to doc/co_labels.rst
index a539acbb7144c1b8df9ee667703df6cf7c05d354..678130dc36d436d3fadd392f3340b83d4cf069b9 100644
--- a/doc/labels.rst
+++ b/doc/co_labels.rst
@@ -1,10 +1,10 @@
 .. _labels:
 
-Labels
-=========
+Cell labels
+===========
 
 Arbor provides a domain specific language (DSL) for describing regions and
-locations on morphologies, and a dictionary for assiciating these descriptions
+locations on morphologies, and a dictionary for associating these descriptions
 with a string label.
 
 The labels are used to refer to regions
@@ -12,7 +12,7 @@ and locations when setting cell properties and attributes.
 For example, the membrane capacitance on a region of the cell membrane, or
 the location of synapse instances.
 
-Example Cell
+Example cell
 ------------
 
 The following morphology is used on this page to illustrate region and location
@@ -24,7 +24,7 @@ descriptions. It has a soma, dendritic tree and an axon with a hillock:
   :width: 800
   :align: left
 
-  Segments of the morphology are colored according to tags:
+  Segments of the morphology are coloured according to tags:
   soma (tag 1, red), axon (tag 2, grey), dendrites (tag 3, blue) (left).
   The 6 branches of the morphology with their branch ids (right).
 
@@ -43,7 +43,7 @@ The other branches in the dendritic tree have the following properties:
 tapers from 4 μm to 0.4 μm attached to the proximal end of the soma; and the start of the
 axon proper with constant radius 0.4 μm.
 
-Label Types
+Label types
 ------------
 
 .. _labels-locset:
@@ -111,6 +111,7 @@ Examples of expressions that define regions include:
 * ``(tag 1)``: all segments with tag 1.
 * ``(branch 2)``: branch 2.
 * ``(region "soma")``: the region with the label "soma".
+* ``"soma"``: a shortcut for the region with the label "soma".
 
 Examples of expressions that define locsets include:
 
@@ -151,12 +152,12 @@ describes the region of all parts of a cell with either tag 3 or tag 4 and radiu
     As a result, label dictionaries are much more concise and easy to interpret for
     consumers of a model than hoc templates.
     Furthermore they are less error prone because
-    Arbor handles generation of conrete cable sections and locations when
+    Arbor handles generation of concrete cable sections and locations when
     expressions are applied to a morphology.
 
 .. _labels-expr-docs:
 
-Expression Syntax
+Expression syntax
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The DSL uses `s-expressions <https://en.wikipedia.org/wiki/S-expression>`_, which are composed of the following basic types:
@@ -200,7 +201,7 @@ dendritic tree where the radius first is less than or equal to 0.2 μm.
     and applicable to arbitrary morphologies.
 
 
-Locset Expressions
+Locset expressions
 ~~~~~~~~~~~~~~~~~~~~~
 
 .. figure:: gen-images/label_branch.svg
@@ -238,7 +239,7 @@ Locset Expressions
 .. label:: (terminal}
 
     The location of terminal points, which are the most distal locations on the morphology.
-    These will typicall correspond to the tips, or end points, of dendrites and axons.
+    These will typically correspond to the tips, or end points, of dendrites and axons.
 
     .. figure:: gen-images/term_label.svg
       :width: 300
@@ -332,8 +333,8 @@ Locset Expressions
 .. label:: (sum lhs:locset rhs:locset [...locset])
 
     Multiset summation of two locsets, such that ``(sum lhs rhs) = A + B``, where A and B are multisets of locations.
-    This is equivalent to contactenating the two lists, and the length of the result is the sum of
-    the lenghts of the inputs. For example:
+    This is equivalent to concatenating the two lists, and the length of the result is the sum of
+    the lengths of the inputs. For example:
 
     .. code-block:: lisp
 
@@ -347,7 +348,7 @@ Locset Expressions
 
         (join (location 1 0.5) (location 2 0.1) (location 1 0.2) (location 1 0.5) (location 4 0))
 
-Region Expressions
+Region expressions
 ~~~~~~~~~~~~~~~~~~~~~
 
 .. label:: (nil)
@@ -615,148 +616,15 @@ Label Dictionaries
 
 *Labels* can be assigned to expressions, and used to refer to the expression or the
 concrete region or locset generated when the expression is applied to a morphology.
-A label is a string with the following rules:
+Although any string is a valid label, it is a good idea to avoid labels that would
+also be valid expressions in the region DSL; creating a label ``"(tag 1)"`` will only
+lead to confusion.
 
-* may contain alpha-numeric values, ``{a-z}[A-z][0-9]``, underscore ``_`` and hyphen ``-``.
-* no leading underscore, hyphen or numeric values: for example ``_myregion``,
-  ``-samples``, and ``2ndpoint`` are invalid labels.
+Labels are stored with their associated expressions as key-value pairs in *label dictionaries*.
 
-labels are stored with their associated expressions as key-value pairs in *label dictionaries*.
 
-Python API
-----------
-
-The ``arbor.label_dict`` type is used for creating and manipulating label dictionaries,
-which can be initialised with a dictionary that defines (label, expression)
-pairs. For example, a dictionary that uses tags that correspond to SWC
-`structure identifiers <http://www.neuronland.org/NLMorphologyConverter/MorphologyFormats/SWC/Spec.html>`_
-to label soma, axon, dendrite and apical dendrites is:
-
-
-.. code-block:: python
-
-    import arbor
-
-    labels = {'soma': '(tag 1)',
-              'axon': '(tag 2)',
-              'dend': '(tag 3)',
-              'apic': '(tag 4)'}
-
-    d = arbor.label_dict(labels)
-
-Alternatively, start with an empty label dictionary and add the labels and
-their definitions one by one:
-
-.. code-block:: python
-
-    import arbor
-
-    d = arbor.label_dict()
-
-    d['soma'] = '(tag 1)'
-    d['axon'] = '(tag 2)'
-    d['dend'] = '(tag 3)'
-    d['apic'] = '(tag 4)'
-
-The square bracket operator is used above to add label definitions. It can
-be used to modify existing definitions, so long as the new new definition has the
-same type (region or locset):
-
-.. code-block:: python
-
-    import arbor
-
-    # A label dictionary that defines the label "dend" that defines a region.
-    d = arbor.label_dict({'dend': '(tag 3)')
-
-    # The definition of a label can be overwritten with a definition of the
-    # same type, in this case a region.
-    d['dend'] = '(join (tag 3) (tag 4))'
-
-    # However, a region can't be overwritten by a locset, or vice-versa.
-    d['dend'] = '(terminal)' # error: '(terminal)' defines a locset.
-
-    # New labels can be added to the dictionary.
-    d['soma'] = '(tag 1)'
-    d['axon'] = '(tag 2)'
-
-    # Square brackets can also be used to get a label's definition.
-    assert(d['soma'] == '(tag 1)')
-
-Expressions can refer to other regions and locsets in a label dictionary.
-In the example below, we define a region labeled *'tree'* that is the union
-of both the *'dend'* and *'apic'* regions.
-
-.. code-block:: python
-
-    import arbor
-
-    d = arbor.label_dict({
-            'soma': '(tag 1)',
-            'axon': '(tag 2)',
-            'dend': '(tag 3)',
-            'apic': '(tag 4)',
-            # equivalent to (join (tag 3) (tag 4))
-            'tree': '(join (region "dend") (region "apic"))'})
-
-The order that labels are defined does not matter, so an expression can refer to a
-label that has not yet been defined:
-
-.. code-block:: python
-
-    import arbor
-
-    d = arbor.label_dict()
-    # 'reg' refers 
-    d['reg'] = '(distal-interval (locset "loc"))'
-    d['loc'] = '(location 3 0.5)'
-
-    # If d was applied to a morphology, 'reg' would refer to the region:
-    #   '(distal-interval (location 3 0.5))'
-    # Which is the sub-tree of the matrix starting at '(location 3 0.5)'
-
-    # The locset 'loc' can be redefined
-    d['loc'] = '(proximal (tag 3))'
-
-    # Now if d was applied to a morphology, 'reg' would refer to:
-    #   '(distal-interval (proximal (tag 3))'
-    # Which is the subtrees that start at the proximal locations of
-    # the region '(tag 3)'
-
-Cyclic dependencies are not permitted, as in the following example where
-two labels refer to one another:
-
-.. code-block:: python
-
-    import arbor
-
-    d = arbor.label_dict()
-    d['reg'] = '(distal-interval (locset "loc"))'
-    d['loc'] = '(proximal (region "reg"))'
-
-    # Error: 'reg' needs the definition of 'loc', which in turn needs the
-    # definition of 'reg'.
-
-.. note::
-    In the example above there will be no error when the label dictionary is defined.
-    Instead, there will be an error later when the label dictionary is applied to
-    a morphology, and the cyclic dependency is detected when concretising the locations
-    in the locsets and the cable segments in the regions.
-
-
-The type of an expression, locset or region, is inferred automatically when it is
-input into a label dictionary.
-Lists of the labels for regions and locsets are available as attributes:
-
-.. code-block:: python
-
-    import arbor
-
-    d = arbor.label_dict({
-            'soma': '(tag 1)',
-            'axon': '(tag 2)',
-            'dend': '(tag 3)',
-            'apic': '(tag 4)',
-            'site': '(location 2 0.5)',
-            'term': '(terminal)'})
+API
+---
 
+* :ref:`Python <py_labels>`
+* :ref:`C++ <cpp_labels>`
diff --git a/doc/co_mechanisms.rst b/doc/co_mechanisms.rst
new file mode 100644
index 0000000000000000000000000000000000000000..2719c0de44778444de0f8007be5483a3883ee33a
--- /dev/null
+++ b/doc/co_mechanisms.rst
@@ -0,0 +1,161 @@
+.. _mechanisms:
+
+Cell mechanisms
+===============
+
+Mechanisms describe biophysical processes such as ion channels and synapses.
+Mechanisms are assigned to regions and locations on a cell morphology
+through a process that is called :ref:`decoration <cablecell-decoration>`.
+Mechanisms are described using a dialect of the :ref:`NMODL <nmodl>` domain
+specific language that is similarly used in `NEURON <https://neuron.yale.edu/neuron/>`_.
+
+Mechanism catalogues
+----------------------
+
+A *mechanism catalogue* is a collection of mechanisms that maintains:
+
+1. A collection of mechanism metadata indexed by name.
+2. A further hierarchy of *derived* mechanisms, that allow specialization of
+   global parameters, ion bindings, and implementations.
+3. A map for looking up a concrete mechanism implementation on a target hardware back end.
+
+Derived mechanisms will always have a different name to the mechanism from which they are derived. This name is given explicitly when the derivation is constructed, or implicitly when a mechanism is :ref:`requested <mechanisms-name-note>` with a name of the form ``"mech/param=value,..."``. In this instance, if a mechanism of that name does not already exist in the catalogue, it will be implicitly derived from an existing mechanism ``"mech"`` with global parameters and ion bindings set according to the assignments following the slash. If the mechanism ``"mech"`` depends upon only a single ion, the name of that ion can be omitted in the assignments: ``"mech/oldion=newion"`` and ``"mech/newion"`` are equivalent derivations.
+
+
+Catalogues provide an interface for querying mechanism metadata, which includes the following information:
+
+* Global parameter names, units, and default values.
+* Range parameter names, units, and default values.
+* State variable names, units and default values.
+* Ion dependencies: for each ion used by the mechanism, information on whether the mechanism writes to its internal or external concentration or to its reversal potential value, and whether it reads or asserts the ionic charge.
+
+Default mechanisms
+''''''''''''''''''
+
+Arbor provides a default catalogue with the following mechanisms:
+
+* *pas*: Leaky current (:ref:`density mechanism <mechanisms-density>`).
+* *hh*:  Classic Hodgkin-Huxley dynamics (:ref:`density mechanism <mechanisms-density>`).
+* *nernst*: Calculate reversal potential for an ionic species using the Nernst equation (:ref:`reversal potential mechanism <mechanisms-revpot>`)
+* *expsyn*: Synapse with discontinuous change in conductance at an event followed by an exponential decay (:ref:`point mechanism <mechanisms-point>`).
+* *exp2syn*: Bi-exponential conductance synapse described by two time constants: rise and decay (:ref:`point mechanism <mechanisms-point>`).
+
+With the exception of *nernst*, these mechanisms are the same as those available in NEURON.
+
+Parameters
+''''''''''
+
+Mechanism behaviour can be tuned using parameters and ion channel dependencies,
+as defined in the NMODL description.
+Parameters and ion species are set initially before a simulation starts, and remain
+unchanged thereafter, for the duration of the simulation.
+There are two types of parameters that can be set by users:
+
+* *Global* parameters are a single scalar value that is the same everywhere a mechanism is defined.
+* *Range* parameters can vary spatially.
+
+Every mechanism is applied to a cell via a *mechanism description*, a
+``(name, range_parameters)`` tuple, where ``name`` is a string,
+and ``range_parameters`` is an optional dictionary of key-value pairs
+that specifies values for range parameters.
+For example, consider a mechanism that models passive leaky dynamics with
+the following parameters:
+
+* *Name*: ``"passive"``.
+* *Global parameter*: reversal potential ``el``, default -65 mV.
+* *Range parameter*: conductance ``g``, default 0.001 S⋅cm⁻².
+
+The following example mechanism descriptions for our passive mechanism show that parameters and
+ion species dependencies only need to be specified when they differ from their defaults:
+
+* ``("passive")``: the passive mechanism with default parameters.
+* ``("passive/el=-80")``: derive a new passive mechanism with a non-default value for global parameter.
+* ``("passive", {"gl": 0.005})``: passive mechanism with a new a non-default range parameter value.
+* ``("passive/el=-80", {"gl": 0.005})``: derive a new passive mechanism that overrides both
+
+Similarly to global parameters, ion species can be renamed in the mechanism name.
+This allows the use of generic mechanisms that can be adapted to a specific species
+during model instantiation.
+For example, the ``nernst`` mechanism in Arbor's default mechanism catalogue calculates
+the reversal potential of a generic ionic species ``x`` according to its internal
+and external concentrations and valence. To specialize ``nernst`` for calcium name it
+``("nernst/x=ca")``, or as there is only one ion species in the mechanism the
+shorthand ``("nernst/ca")`` can be used unambiguously.
+
+.. _mechanisms-name-note:
+
+.. note::
+    Global parameter values and ionic dependencies are the same for each instance of
+    a mechanism; changing these requires the derivation of a new mechanism, implicitly or explicitly.
+    For this reason, new global parameter values and ion renaming are part of the name of
+    the new mechanism, or a mechanism with a new unique name must be defined.
+
+
+Mechanism types
+---------------
+
+There are two broad categories of mechanism, density mechanisms and
+point mechanisms, and a third special density mechanism for
+computing ionic reversal potentials.
+
+.. _mechanisms-density:
+
+Density mechanisms
+''''''''''''''''''''''
+
+Density mechanisms are :ref:`NMODL mechanisms <nmodl>`
+which describe biophysical processes that are distributed in space, but whose behaviour
+is defined purely by the state of the cell and the process at any given point.
+
+Density mechanisms are commonly used to describe ion channel dynamics,
+for example the ``hh`` and ``pas`` mechanisms provided by NEURON and Arbor,
+which model classic Hodgkin-Huxley and passive leaky currents respectively.
+
+.. _mechanisms-revpot:
+
+Ion reversal potential mechanisms
+'''''''''''''''''''''''''''''''''
+
+These mechanisms, which describe ionic reversal potential
+behaviour, can be specified for cells or the whole model.
+
+The reversal potential of an ion species is calculated by an
+optional *reversal potential mechanism*.
+If no such mechanism is specified for an ion species, the initial
+reversal potential values are maintained for the course of a simulation.
+Otherwise, the mechanism does the work.
+
+Reversal potential mechanisms are density mechanisms subject to some strict restrictions.
+Specifically, a reversal potential mechanism described in NMODL:
+
+* May not maintain any state variables.
+* Can only write to the reversal potential (``eX``) value of the ion species.
+* Can not be a :ref:`point mechanism <mechanisms-point>`.
+
+Essentially, reversal potential mechanisms must be pure functions of cellular
+and ionic state.
+
+.. note::
+    Arbor imposes greater restrictions on mechanisms that update ionic reversal potentials
+    than NEURON. Doing so simplifies reasoning about interactions between
+    mechanisms that share ionic species, by virtue of having one mechanism, and one
+    mechanism only, that calculates reversal potentials according to concentrations
+    that the other mechanisms use and modify.
+
+.. _mechanisms-point:
+
+Point mechanisms
+'''''''''''''''''''''''''''''''''
+
+*Point mechanisms*, which are associated with connection end points on a
+cable cell, are placed at discrete locations on the cell.
+Unlike density mechanisms, whose behaviour is defined purely by the state of the cell
+and the process, their behaviour is additionally governed by the timing and weight of
+events delivered via incoming connections.
+
+
+API
+---
+
+* :ref:`Python <py_mechanisms>`
+* :ref:`C++ <cpp_mechanisms>`
diff --git a/doc/morphology.rst b/doc/co_morphology.rst
similarity index 55%
rename from doc/morphology.rst
rename to doc/co_morphology.rst
index a0338c23b6d31df8ee3fe879c7133d6265e7de9a..f07cbfe0502b54d712847842bf875a39bcb567bf 100644
--- a/doc/morphology.rst
+++ b/doc/co_morphology.rst
@@ -1,13 +1,13 @@
-.. _morphology:
+.. _co_morphology:
 
-Morphology
-==========
+Cell morphology
+===============
 
 A cell's *morphology* describes both its geometry and branching structure.
-Morphologies in Arbor are modeled as a set of one dimensional cables of variable radius,
+Morphologies in Arbor are modelled as a set of one dimensional cables of variable radius,
 joined together to form a tree.
 
-The building blocks of morpholgies tree are points and segments.
+The building blocks of morphology tree are points and segments.
 A *point* is a three-dimensional location and a radius, used to mark the centre and radius
 of the cable.
 
@@ -22,7 +22,9 @@ of the cable.
 
 
 A *segment* is a frustum (cylinder or truncated cone), with the centre and radius at each
-end defined by a pair of points.
+end defined by a pair of points. In other words, in Arbor the radius between two points is interpolated
+linearly, resulting in either a cylinder (equal radii) or truncated cone (differing radii),
+centred at the line through the pair of points.
 
 .. csv-table::
    :widths: 10, 10, 30
@@ -41,7 +43,7 @@ to SWC `structure identifiers <http://www.neuronland.org/NLMorphologyConverter/M
 
 .. _morph-segment_tree:
 
-Segment Trees
+Segment trees
 --------------
 
 A *segment tree* describes a morphology as a set of segments and their connections,
@@ -54,7 +56,7 @@ together with a parent-child adjacency relationship where a child segment is
 distal to its parent.
 Branches in the tree occur where a segment has more than one child.
 Furthermore, a segment can not have more than one parent.
-In this manner, neuron morphologies are modeled as a *tree*, where cables that
+In this manner, neuron morphologies are modelled as a *tree*, where cables that
 represent dendrites and axons can branch, but branches can not rejoin.
 
 .. _morph-segment-definitions:
@@ -77,9 +79,9 @@ The following definitions are used to refer to segments in a segment tree:
   * Arbor allows more than two branches at a fork point.
 
 The following segment tree models a soma as a cylinder, a branching dendritic tree and
-an axon with an axonal hillock. The segments are colored according to their tag, which
-in this case are SWC structure identifiers: tag 1 colored pink for soma;
-tag 2 colored grey for axon; tag 3 colored blue for basal dendrites.
+an axon with an axonal hillock. The segments are coloured according to their tag, which
+in this case are SWC structure identifiers: tag 1 coloured pink for soma;
+tag 2 coloured grey for axon; tag 3 coloured blue for basal dendrites.
 
 .. _morph-label-seg-fig:
 
@@ -96,7 +98,7 @@ We can apply the following labels to the segments:
 * The proximal ends of segments 0 and 9 (the soma and axon hillock respectively) are attached to the root of the tree.
 * Segment 2 is a fork, with segments 3 and 5 as children.
 * Segment 5 is a fork, with segments 6 and 7 as children.
-* There is also a fork at the root, whith segments 0 and 9 as children.
+* There is also a fork at the root, with segments 0 and 9 as children.
 * Segments 4, 6, 8 and 10 are terminal segments.
 
 In the example above there are no gaps between segments, however
@@ -121,7 +123,7 @@ axon and dendritic tree and the soma segment to which they attach.
 
     A gap between a cylindrical soma and segments attached to it does not mean
     that the segmentation is invalid.
-    To illustrate why this can occur, consider a potato-shaped soma modeled with a
+    To illustrate why this can occur, consider a potato-shaped soma modelled with a
     cylinder of the same surface area.
     If the cell description places the first segment of a dendritic tree where it attaches to
     the "potato soma", it is unlikely to be collocated with an end of the simplified soma.
@@ -140,18 +142,18 @@ uses 4 segments to model the soma.
 
 .. _morph-morphology:
 
-Morphology
-----------
+Geometry
+--------
 
 A *morphology* describes the geometry of a cell as unbranched cables with variable radius
-, and their associated tree structure. 
+, and their associated tree structure.
 Every segment tree can be used to generate a unique morphology, which derives and enumerates
 *branches* from the segments.
 The branches of a morphology are unbranched cables, composed of one or more segments, where:
 
   * the first (proximal) segment of the branch is either a root or the child of fork segment;
   * the last (distal) segment of the branch is either a fork or terminal segment;
-  * branches are enumerated according to the ids of their proximal segments in the segment trree.
+  * branches are enumerated in order, following the order of the ids of their proximal segments in the segment tree.
 
 When constructed in this manner, the following statements are true for the branches and
 their enumeration:
@@ -169,11 +171,9 @@ their enumeration:
 .. Note::
 
     Because two topologically-equivalent morphologies may have different segment and
-    branch numbering, it is important that model descriptions should avoid refering to
+    branch numbering, it is important that model descriptions should avoid referring to
     branches or segments by id.
-    This should be relaxed only in well-understood situations, for example when working with
-    models that always represent to soma with a single segment at the root of the tree,
-    which will always have segment id 0.
+    This should only be relaxed when the configuration of branches in a particular morphology is known exactly and unambiguously.
 
 To illustrate branch generation, consider the first segment tree example on this page,
 which is illustrated along with its branches below.
@@ -192,7 +192,7 @@ There are four more branches in the dendritic tree, and one representing the two
 segments of the axon.
 
 Note, that though it is possible to create an unbranched sequence of segments composed
-of the axon, soma and first two segements in the dendritic tree, this sequence is decomposed
+of the axon, soma and first two segments in the dendritic tree, this sequence is decomposed
 as two branches because segments 0 (soma) and 9 (first segment in axon) are at the
 root of the tree.
 
@@ -201,7 +201,7 @@ Every branch has one parent, with branches at the root of the tree having the pl
 parent index :data:`mnpos <arbor.mnpos>`. Segments can have any non-negative number of children,
 however by nature of their construction, no branch can have only one child: a branch has
 either no children, or two or more children.
-The parent-child information and segments for the morphology are summarised:
+The parent-child information and segments for the morphology are summarized:
 
 .. csv-table::
    :widths: 10, 10, 10, 10
@@ -216,7 +216,7 @@ The parent-child information and segments for the morphology are summarised:
 
 Gaps between segments do not influence branch creation, hence branches
 can contain gaps between segments. Take the example of a morphology with
-a gap between the soma and the axona and dendritic trees:
+a gap between the soma and the axon and dendritic trees:
 
 .. figure:: gen-images/detached_morph.svg
   :width: 800
@@ -261,7 +261,7 @@ multiple soma and dendrite segments in branch 0.
 .. Note::
     Arbor provides a consistent representation of morphologies with no
     special cases for concepts like magical soma branches, in order to
-    build reproducable and consistent model descriptions.
+    build reproducible and consistent model descriptions.
 
     Users of NEURON who are used to creating a separate soma section
     that is always the first section in a morphology should not
@@ -270,7 +270,7 @@ multiple soma and dendrite segments in branch 0.
 
     The soma in the examples above can be referred to in later model
     building phases, for example when describing the distribution of
-    ion channels, by using refering to all parts of the cell with
+    ion channels, by using referring to all parts of the cell with
     :ref:`tag 1 <labels-expressions>`.
 
 
@@ -285,12 +285,12 @@ The examples use the Python API are two-dimensional, with the z-dimension set to
 Example 1: Spherical cell
 """"""""""""""""""""""""""""""
 
-A simple model of a cell as a sphere can be modeled using a cylinder with length
+A simple model of a cell as a sphere can be modelled using a cylinder with length
 and diameter equal to the diameter of the sphere, which will have the same
 surface area (disregarding the area of the cylinder's circular ends).
 
 Here a cylinder of length and diameter 5 μm is used to represent a *spherical cell*
-with a radius of 2 μm, centered at the origin.
+with a radius of 2 μm, centred at the origin.
 
 .. code:: Python
 
@@ -325,7 +325,7 @@ This can be described using a single segment.
 
   A tapered cable with one cable segment (left), generates a morphology with one branch (right).
 
-The radius of a cable segment varies lineary between its end points. To define an unbranched cable
+The radius of a cable segment varies linearly between its end points. To define an unbranched cable
 with irregular radius and "squiggly" shape, use multiple segments to build a piecewise linear reconstruction
 of the cable geometry.
 This example starts and ends at the same locations as the previous, however it is constructed from 4
@@ -340,14 +340,12 @@ distinct cable segments:
     tree.append(2,     mpoint( 8.0,  0.0,  0.0, 0.6), mpoint(10.0,  0.0,  0.0, 0.5), tag=3)
     morph = arbor.morphology(tree)
 
-    morph = arbor.morphology(tree)
-
 .. figure:: gen-images/branch_morph2.svg
   :width: 600
   :align: center
 
-  The morphology is an ubranched cable comprised of 4 cable segments,
-  colored according to their tags: tag 1 red; tag 2 gree; tag 3 blue (left).
+  The morphology is an unbranched cable comprised of 4 cable segments,
+  coloured according to their tags: tag 1 red; tag 2 green; tag 3 blue (left).
   The four segments form one branch (right).
 
 Gaps are possible between two segments. The example below inserts a 1 μm gap between the second
@@ -369,7 +367,7 @@ joining the segments together, such that the morphology with the gap is the same
 
   There is a gap between segment 1 and segment 2 (left), and there is a single branch (right).
 
-The radius of a cable is piecewise linear, with discontinuities permited at the
+The radius of a cable is piecewise linear, with discontinuities permitted at the
 interface between segments.
 The next example adds a discontinuity to the previous example between segments
 3 and 4, where the radius changes from 0.5 μm to 0.3 μm:
@@ -441,8 +439,8 @@ diameter equal to 6 μm, which has the same surface area as the sphere.
 
   Note that branch 0 (right) is composed of segments 0, 1, and 2 (left).
 
-The soma is the first segment, labeled with tag 1. The dendritic tree is a simple
-y-shaped tree composed of 4 segments, each labeled with tag 3.
+The soma is the first segment, labelled with tag 1. The dendritic tree is a simple
+y-shaped tree composed of 4 segments, each labelled with tag 3.
 The first branch is composed of 3 segments: the soma segment and the first two segments
 in the dendritic tree because the segments have parent child ordering and no fork points.
 
@@ -469,7 +467,7 @@ because it has two children: the dendrites attached to its distal end.
 
 
 .. note::
-    The discretization process, which converts segments and branches into compartments,
+    The discretisation process, which converts segments and branches into compartments,
     will ignore gaps between segments in the input. The cell below, in which the dendrites
     and axon have been translated to remove any gaps, is equivalent to the previous example
     for the back end simulator.
@@ -484,372 +482,9 @@ because it has two children: the dendrites attached to its distal end.
       :width: 900
       :align: center
 
-Python API
-----------
-
-.. currentmodule:: arbor
-
-.. data:: mnpos
-    :type: int
-
-    Value used to indicate "no parent" in :class:`segment_tree` and :class:`morphology`
-    trees of segments and branches respectively.
-
-    .. code-block:: python
-
-        import arbor
-
-        tree = arbor.segment_tree()
-
-        # mnpos can be used to explicitly specify that a segment
-        # is at the root of the tree. More than one segment can
-        # be at the root, and they will all be joined electrically
-        # at their proximal ends.
-        tree.append(parent=arbor.mnpos, # attach segment to root.
-                    prox=arbor.mpoint(0, 0,-5, 5),
-                    dist=arbor.mpoint(0, 0, 5, 5),
-                    tag=1)
-        tree.append(parent=0,
-                    prox=arbor.mpoint(0, 0, 5, 0.5),
-                    dist=arbor.mpoint(0, 0,50, 0.2),
-                    tag=3)
-
-        # mnpos can also be used when querying a sample_tree or morphology,
-        # for example the following snippet that finds all branches in the
-        # morphology that are attached to the root of the morphology.
-        m = arbor.morphology(tree)
-        base_branches = [i for i in range(m.num_branches)
-                            if m.branch_parent(i) == arbor.mnpos]
-
-        print(base_branches)
-
-
-
-.. class:: location
-
-    A location on :attr:`branch`, where :attr:`pos`, in the range ``0 ≤ pos ≤ 1``,
-    gives the relative position
-    between the proximal and distal ends of the branch. The position is in terms
-    of branch path length, so for example, on a branch of path length 100 μm ``pos=0.2``
-    corresponds to 20 μm and 80 μm from the proximal and distal ends of the
-    branch respectively.
-
-    .. function:: location(branch, pos)
-
-        Constructor.
-
-    .. attribute:: branch
-        :type: int
-
-        The branch id of the location.
-
-    .. attribute:: pos
-        :type: float
-
-        The relative position of the location on the branch.
-
-.. class:: cable
-
-    An unbranched cable that is a subset of a branch.
-    The values of ``0 ≤ prox ≤ dist ≤ 1`` are the relative position
-    of the cable's end points on the branch, in terms
-    of branch path length. For example, on a branch of path length 100 μm, the values
-    :attr:`prox` =0.2, :attr:`dist` =0.8 describe a cable that starts and
-    ends 20 μm and 80 μm along the branch respectively.
-
-    .. function:: cable(branch, prox, dist)
-
-        Constructor.
-
-    .. attribute:: branch
-        :type: int
-
-        The branch id of the cable.
-
-    .. attribute:: prox
-        :type: float
-
-        The relative position of the proximal end of the cable on the branch.
-
-    .. attribute:: dist
-        :type: float
-
-        The relative position of the distal end of the cable on the branch.
-
-.. class:: mpoint
-
-    A location of a cell morphology at a fixed location in space. Describes the location
-    of the as three-dimensional coordinates (:attr:`x`, :attr:`y`, :attr:`z`) and
-    the :attr:`radius` of the cable.
-
-    .. attribute:: x
-        :type: real
-
-        X coordinate (μm)
-
-    .. attribute:: y
-        :type: real
-
-        Y coordinate (μm)
-
-    .. attribute:: z
-        :type: real
-
-        x coordinate (μm)
-
-    .. attribute:: radius
-        :type: real
-
-        Radius of the cable (μm)
-
-.. class:: segment
-
-    .. attribute:: prox
-        :type: mpoint
-
-        The location and radius at the proximal end of the segment.
-
-    .. attribute:: dist
-        :type: mpoint
-
-        The location and radius at the distal end of the segment.
-
-    .. attribute:: tag
-        :type: int
-
-        Integer tag meta-data associated with the segment.
-        Typically the tag would correspond to the SWC structure identifier:
-        soma=1, axon=2, dendrite=3, apical dendrite=4, however arbitrary
-        tags, including zero and negative values, can be used.
-
-.. class:: segment_tree
-
-    A segment tree is a description of a the segments and their connections
-    Segment trees comprise a sequence of segments starting from at lease one *root* segment,
-    together with a parent-child adjacency relationship where a child segment is
-    distal to its parent.
-    Branches in the tree occur where a segment has more than one child.
-    Furthermore, a segment can not have more than one parent.
-    In this manner, neuron morphologies are modeled as a *tree*, where cables that
-    represent dendrites and axons can branch, but branches can not rejoin.
-    A segment tree is a segment-based description of a cell's morphology.
-
-    .. function:: segment_tree()
-
-        Construct an empty segment tree.
-
-    The tree is constructed by *appending* segments to the tree.
-    Segments are numbered starting at 0 in the order that they are added,
-    with the first segment getting id 0, the second segment id 1, and so forth.
-
-    A segment can not be added before its parent, hence the first segment
-    is always at the root. In this manner, a segment tree is
-    always guarenteed to be in a correct state, with consistent parent-child
-    indexing, and with *n* segments numbered from *0* to *n-1*.
-
-    To illustrate how a segment tree is constructed by appending segments,
-    take the segment tree used in the :ref:`documentation above <morph-label-seg-fig>`.
-
-
-    .. figure:: gen-images/label_seg.svg
-
-
-    Which is constructed as follows.
-
-    .. _morph-label-seg-code:
-
-    .. code-block:: Python
-
-        import arbor
-        from arbor import mpoint
-        from arbor import mpos
-
-        tree = arbor.segment_tree()
-        # Start with a cylinder segment for the soma (with tag 1)
-        tree.append(mnpos, mpoint(0,   0.0, 0, 2.0), mpoint( 4,  0.0, 0, 2.0), tag=1)
-        # Construct the first section of the dendritic tree,
-        # comprised of segments 1 and 2, attached to soma segment 0.
-        tree.append(0,     mpoint(4,   0.0, 0, 0.8), mpoint( 8,  0.0, 0, 0.8), tag=3)
-        tree.append(1,     mpoint(8,   0.0, 0, 0.8), mpoint(12, -0.5, 0, 0.8), tag=3)
-        # Construct the rest of the dendritic tree.
-        tree.append(2,     mpoint(12, -0.5, 0, 0.8), mpoint(20,  4.0, 0, 0.4), tag=3)
-        tree.append(3,     mpoint(20,  4.0, 0, 0.4), mpoint(26,  6.0, 0, 0.2), tag=3)
-        tree.append(2,     mpoint(12, -0.5, 0, 0.5), mpoint(19, -3.0, 0, 0.5), tag=3)
-        tree.append(5,     mpoint(19, -3.0, 0, 0.5), mpoint(24, -7.0, 0, 0.2), tag=3)
-        tree.append(5,     mpoint(19, -3.0, 0, 0.5), mpoint(23, -1.0, 0, 0.2), tag=3)
-        tree.append(7,     mpoint(23, -1.0, 0, 0.2), mpoint(26, -2.0, 0, 0.2), tag=3)
-        # Two segments that define the axon, with the first at the root, where its proximal
-        # end will be connected with the proximal end of the soma segment.
-        tree.append(mnpos, mpoint(0,   0.0, 0, 2.0), mpoint(-7,  0.0, 0, 0.4), tag=2)
-        tree.append(9,     mpoint(-7,  0.0, 0, 0.4), mpoint(-10, 0.0, 0, 0.4), tag=2)
-
-    .. method:: append(parent, prox, dist, tag)
-
-        Append a segment to the tree.
-
-        :return: index of the new segment
-        :param int parent: index of segment
-        :param mpoint prox: proximal end of the segment
-        :param mpoint dist: distal end of the segment
-        :param int tag: tag meta data of segment
-
-    .. method:: append(parent, dist, tag)
-        :noindex:
-
-        Append a segment to the tree whose proximal end has the location and
-        radius of the distal end of the parent segment.
-
-        This version of append can't be used for a segment at the root of the
-        tree, that is, when ``parent`` is :data:`mnpos`, in which case both proximal
-        and distal ends of the segment must be specified.
-
-        :return: index of the new segment
-        :param int parent: index of segment
-        :param mpoint dist: distal end of the segment
-        :param int tag: tag meta data of segment
-
-    .. method:: append(parent, x, y, z, radius, tag)
-        :noindex:
-
-        Append a segment to the tree whose proximal end has the location and
-        radius of the distal end of the parent segment.
-
-        This version of append can't be used for a segment at the root of the
-        tree, that is, when ``parent`` is :data:`mnpos`, in which case both proximal
-        and distal ends of the segment must be specified.
-
-        :return: index of the new segment
-        :param int parent: index of segment
-        :param float x: distal x coordinate (μm)
-        :param float y: distal y coordinate (μm)
-        :param float z: distal z coordinate (μm)
-        :param float radius: distal radius (μm)
-        :param int tag: tag meta data of segment
-
-    .. attribute:: empty
-        :type: bool
-
-        If the tree is empty (i.e. whether it has size 0)
-
-    .. attribute:: size
-        :type: int
-
-        The number of segments.
-
-    .. attribute:: parents
-        :type: list
-
-        A list of parent indexes of the segments.
-
-    .. attribute:: segments
-        :type: list
-
-        A list of the segments.
-
-.. py:function:: load_swc(filename)
-
-    Loads the morphology in an SWC file as a :class:`segment_tree`.
-
-    The samples in the SWC files are treated as the end points of segments, where a
-    sample and its parent form a segment.
-    The :py:attr:`tag <segment.tag>` of each segment is the
-    `structure identifier <http://www.neuronland.org/NLMorphologyConverter/MorphologyFormats/SWC/Spec.html>`_
-    of the distal sample.
-    The structure identifier of the first (root) sample is ignored, as it can only be the
-    proximal end of any segment.
-
-    .. note::
-        This method does not interpret the first sample, typically associated with the soma,
-        as a sphere. SWCs with single point somas are, unfortunately, reasonably common, for example
-        `SONATA <https://github.com/AllenInstitute/sonata/blob/master/docs/SONATA_DEVELOPER_GUIDE.md#representing-biophysical-neuron-morphologies>`_
-        model descriptions.
-
-        Such representations are unfortunate because simulation tools like Arbor and NEURON require
-        the use of cylinders or fustrums to describe morphologies, and it is not possible to
-        infer how branches attached to the soma should be connected.
-
-        The :func:`load_swc_allen` function provides support for interpreting
-        such SWC files.
-
-
-    :param str filename: the name of the SWC file.
-    :rtype: segment_tree
-
-.. py:function:: load_swc_allen(filename, no_gaps=False)
-
-    Generate a segment tree from an SWC file following the rules prescribed by
-    AllenDB and Sonata. Specifically:
-
-        * The first sample (the root) is treated as the center of the soma.
-        * The morphology is translated such that the soma is centered at (0,0,0).
-        * The first sample has tag 1 (soma).
-        * All other samples have tags 2, 3 or 4 (axon, apic and dend respectively)
-
-    SONATA prescribes that there should be no gaps, however some models in AllenDB
-    have gaps between the start of sections and the soma. The ``no_gaps`` flag can be
-    used to enforce this requirement.
-
-    Arbor does not support modelling the soma as a sphere, so a cylinder with length
-    equal to the soma diameter is used. The cylinder is centered on the origin, and
-    aligned along the z axis.
-    Axons and apical dendrites are attached to the proximal end of the cylinder, and
-    dendrites to the distal end, with a gap between the start of each branch and the
-    end of the soma cylinder to which it is attached.
-
-    :param str filename: the name of the SWC file.
-    :param bool no_gaps: enforce that distance between soma center and branches attached to soma is the soma radius.
-    :rtype: segment_tree
-
-.. py:class:: morphology
-
-    A *morphology* describes the geometry of a cell as unbranched cables
-    with variable radius and their associated tree structure.
-
-    .. note::
-        A morphology takes a segment tree and construct the cable branches.
-        Meta data about branches and their properties that may be expensive to calculate
-        is stored for fast look up during later stages of model building, and
-        querying by users.
-
-        For this reason, morpholgies are read only. To change a morphology, a new
-        morphology should be created using a new segment tree.
-
-    There is one *constructor* for a morphology:
-
-    .. function:: morphology(segment_tree)
-
-        Construct from a segment tree.
-
-    The morphology provides an interface for querying morphology properties:
-
-    .. attribute:: empty
-            :type: bool
-
-            Indicates if the morphology is empty.
-
-    .. attribute:: num_branches
-            :type: int
-
-            The number of branches in the morphology.
-
-    .. method:: branch_parent(i)
-
-            The parent branch of a branch.
-
-            :param int i: branch index
-            :rtype: int
-
-    .. method:: branch_children(i)
-
-            The child branches of a branch.
-
-            :param int i: branch index
-            :rtype: list
-
-    .. method:: branch_segments(i)
-
-            A list of the segments in a branch, ordered from proximal to distal.
 
-            :param int i: branch index
-            :rtype: list
+API
+---
 
+* :ref:`Python <py_morphology>`
+* :ref:`C++ <cpp_morphology>`
diff --git a/doc/model_intro.rst b/doc/co_overview.rst
similarity index 96%
rename from doc/model_intro.rst
rename to doc/co_overview.rst
index 38685a8fdbdc9d5c31518dbd39e48f9561992a99..a962ae4aebf3f3b782d2080aac12fc58110fb49b 100644
--- a/doc/model_intro.rst
+++ b/doc/co_overview.rst
@@ -20,4 +20,4 @@ A cell represents the smallest unit of computation and forms the smallest unit o
 
 :ref:`modelsimulation` manage the instantiation of the model and the scheduling of spike exchange as well as the integration for each cell group. A cell group represents a collection of cells of the same type computed together on the GPU or CPU. The partitioning into cell groups is provided by :ref:`modeldomdec` which describes the distribution of the model over the locally available computational resources.
 
-In order to visualise the result of detected spikes a spike recorder can be used and to analyse Arbor's performance a meter manager is available.
+In order to visualize the result of detected spikes a spike recorder can be used and to analyse Arbor's performance a meter manager is available.
diff --git a/doc/model_recipe.rst b/doc/co_recipe.rst
similarity index 79%
rename from doc/model_recipe.rst
rename to doc/co_recipe.rst
index de8b1e00351be76142e55b4d8230832f3db2353f..80fb9d47e1989f57c8a32d9cec652d4ebde0fb9d 100644
--- a/doc/model_recipe.rst
+++ b/doc/co_recipe.rst
@@ -15,7 +15,7 @@ building phase to provide information about cells in the model, such as:
   * incoming network connections from other cells terminating on a cell;
   * gap junction connections on a cell.
 
-Why Recipes?
+Why recipes?
 --------------
 
 The interface and design of Arbor recipes was motivated by the following aims:
@@ -51,14 +51,24 @@ The steps of building a simulation from a recipe are:
     local cell groups and the part of the communication network by querying the recipe
     for more information about the cells assigned to it.
 
+.. Note::
+    An example of how performance considerations impact Arbor's architecture: you will notice cell kind and cell description are separately added to a recipe. Consider the following conversation between an Arbor simulation, recipe and hardware back-end:
 
-General Best Practices
+    | Simulator: give me cell 37.
+    | Recipe: here you go, it's of C++ type s3cr1ts4uc3.
+    | Simulator: wot? What is the cell kind for cell 37?
+    | Recipe: it's a foobar.
+    | Simulator: Okay. Cell group implementations: which one of you lot deals with foobars?
+    | Foobar_GPUFTW_lolz: That'd be me, if we've got GPU enabled.
+    | Simulator: Okay it's up to you then to deal with this s3cr1ts4uc3 object.
+
+General best practices
 ----------------------
 
 .. topic:: Think of the cells
 
     When formulating a model, think cell-first, and try to formulate the model and
-    the associated workflow from a cell-centered perspective. If this isn't possible,
+    the associated workflow from a cell-centred perspective. If this isn't possible,
     please contact the developers, because we would like to develop tools that help
     make this simpler.
 
@@ -86,15 +96,8 @@ General Best Practices
     to seed random number generators, including those for :cpp:type:`event_generator` s.
 
 
-Mechanisms
-----------------------
-The description of multi-compartment cells also includes the specification of ion channel and synapse dynamics.
-In the recipe, these specifications are called *mechanisms*.
-Implementations of mechanisms are either hand-coded or a translator (modcc) is used to compile a
-subset of NEURON's mechanism specification language NMODL.
-
-Examples
-    Common examples are the *passive/ leaky integrate-and-fire* model, the *Hodgkin-Huxley* mechanism, the *(double-) exponential synapse* model, or the *Natrium current* model for an axon.
+API
+---
 
-Detailed documentation for Python recipes can be found in :ref:`pyrecipe`.
-C++ :ref:`cpprecipe` are documented and best practices are shown as well.
+* :ref:`Python <pyrecipe>`
+* :ref:`C++ <cpprecipe>`
diff --git a/doc/model_simulation.rst b/doc/co_simulation.rst
similarity index 91%
rename from doc/model_simulation.rst
rename to doc/co_simulation.rst
index 1e64a3b15b4df8b5092c1eec27071fbad8d634eb..f5d288420e744a8e1eb6a559e5c07bbe68644639 100644
--- a/doc/model_simulation.rst
+++ b/doc/co_simulation.rst
@@ -25,5 +25,8 @@ Simulations provide an interface for executing and interacting with the model:
     * The model state can be *reset* to its initial state before the simulation was started.
     * *Sampling* of the simulation state can be performed during execution with samplers and probes (e.g. compartment voltage and current) and spike output with the total number of spikes generated since either construction or reset.
 
-The documentation for Arbor's Python simulation interface can be found in :ref:`pysimulation`.
-See :ref:`cppsimulation` for documentation of the C++ simulation API.
+API
+---
+
+* :ref:`Python <pysimulation>`
+* :ref:`C++ <cppsimulation>`
diff --git a/doc/conf.py b/doc/conf.py
index ac511766a7be0c685643f4789b68d1fd631b2b7e..86c5856b33c3a80c58d60f898e112fc930d1761e 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -1,6 +1,13 @@
 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
+import sys, os
 
+# Path to Python Binding (_arbor)
+try:
+    sys.path.append(os.path.join(os.environ['OLDPWD'],"python"))
+    import arbor
+except ImportError:
+    autodoc_mock_imports = ['arbor._arbor']
 
 html_static_path = ['static']
 
@@ -10,13 +17,16 @@ def setup(app):
     app.add_object_type('label', 'lab', 'pair: %s; label')
 
 extensions = [
+    'sphinx.ext.autodoc',
     'sphinx.ext.todo',
     'sphinx.ext.mathjax',
+    'sphinx.ext.coverage'
 ]
 source_suffix = '.rst'
 master_doc = 'index'
 
-html_logo = 'images/arbor-logo.svg'
+html_logo = 'images/arbor-lines-proto-colour.svg'
+html_favicon = 'images/arbor-lines-proto-colour-notext.svg'
 
 project = 'Arbor'
 copyright = '2017, ETHZ & FZ Julich'
@@ -34,20 +44,18 @@ pygments_style = 'perldoc'
 
 # Generate images for the documentation.
 print("--- generating images ---")
-import sys, os
-
-this_path=os.path.split(os.path.abspath(__file__))[0]
 
 # Location of scripts used to generate images
+this_path=os.path.split(os.path.abspath(__file__))[0]
 script_path=this_path+'/scripts'
 sys.path.append(script_path)
+import make_images
 
 # Output path for generated images
 img_path=this_path+'/gen-images'
 if not os.path.exists(img_path):
     os.mkdir(img_path)
 
-import make_images
 make_images.generate(img_path)
 
 print("-------------------------")
diff --git a/doc/cpp_cable_cell.rst b/doc/cpp_cable_cell.rst
index 8083fe7e2afd8631a7452bad5476c87bc7844515..6e2db22ee3b5e43f20d26246e24bd19ea3400d55 100644
--- a/doc/cpp_cable_cell.rst
+++ b/doc/cpp_cable_cell.rst
@@ -1,7 +1,7 @@
-.. _cppcablecell:
+.. _cppcable_cell:
 
 Cable cells
-===============
+===========
 
 .. Warning::
    The interface for building and modifying cable cell objects
@@ -43,8 +43,8 @@ are specified via the ``place`` method. See :ref:`cable-cell-dynamics`, below.
 
 .. _morphology-construction:
 
-Constucting cell morphologies
------------------------------
+Constructing cell morphologies
+------------------------------
 
 .. todo::
 
@@ -208,7 +208,7 @@ cable cell, are attached to a cell with:
 
 .. _electrical-properties:
 
-Electrical properities and ion values
+Electrical properties and ion values
 -------------------------------------
 
 On each cell segment, electrical and ion properties can be specified by the
@@ -222,7 +222,7 @@ value should be taken from the cell or global parameter set.
 
    .. cpp:member:: std::unordered_map<std::string, cable_cell_ion_data> ion_data
 
-   The keys of this map are names of ions, whose parameters will be locally overriden.
+   The keys of this map are names of ions, whose parameters will be locally overridden.
    The struct :cpp:type:`cable_cell_ion_data` has three fields:
    :cpp:type:`init_int_concentration`, :cpp:type:`init_ext_concentration`, and
    :cpp:type:`init_reversal_potential`.
@@ -246,9 +246,9 @@ value should be taken from the cell or global parameter set.
 
    Local areal capacitance of the cell membrane, in Farads per square metre.
 
-   .. cpp:member:: util::optional<cv_policy> discretization
+   .. cpp:member:: util::optional<cv_policy> discretisation
 
-   Method by which CV boundaries are determined when the cell is discretized.
+   Method by which CV boundaries are determined when the cell is discretised.
    See :ref:`cv-policies`.
 
 Default parameters for a cell are given by the :cpp:expr:`default_parameters`
@@ -353,7 +353,7 @@ the global parameters via
 
 This mechanism has global scalar parameters for the gas constant *R* and
 Faraday constant *F*, corresponding to the exact values given by the 2019
-redifinition of the SI base units. These values can be changed in a derived
+redefinition of the SI base units. These values can be changed in a derived
 mechanism in order to use, for example, older values of these physical
 constants.
 
@@ -432,7 +432,7 @@ Cable cell probe addresses that are described by a ``locset`` may generate more
 than one concrete probe: there will be one per location in the locset that is
 satisfiable. Sampler callback functions can distinguish between different
 probes with the same address and id by examining their index and/or
-probe-sepcific metadata found in the ``probe_metadata`` parameter.
+probe-specific metadata found in the ``probe_metadata`` parameter.
 
 Membrane voltage
 ^^^^^^^^^^^^^^^^
@@ -458,7 +458,7 @@ Queries cell membrane potential across whole cell.
 
 *  Sample value: ``cable_sample_range``. Each value is the
    average membrane potential in millivolts across an unbranched
-   component of the cell, as determined by the discretization.
+   component of the cell, as determined by the discretisation.
 
 *  Metadata: ``mcable_list``. Each cable in the cable list describes
    the unbranched component for the corresponding sample value.
@@ -490,7 +490,7 @@ Transmembrane current
         std::string ion;
     };
 
-Membrance current density attributed to a particular ion at
+Membrane current density attributed to a particular ion at
 each site in ``locations``.
 
 *  Sample value: ``double``. Current density in amperes per square metre.
@@ -508,7 +508,7 @@ Membrane current attributed to a particular ion across components of the cell.
 
 *  Sample value: ``cable_sample_range``. Each value is the current in
    nanoamperes across an unbranched component of the cell, as determined
-   by the discretization.
+   by the discretisation.
 
 *  Metadata: ``mcable_list``. Each cable in the cable list describes
    the unbranched component for the corresponding sample value.
@@ -535,7 +535,7 @@ Membrane current _excluding_ capacitive currents across components of the cell.
 
 *  Sample value: ``cable_sample_range``. Each value is the current in
    nanoamperes across an unbranched component of the cell, as determined
-   by the discretization.
+   by the discretisation.
 
 *  Metadata: ``mcable_list``. Each cable in the cable list describes
    the unbranched component for the corresponding sample value.
@@ -549,7 +549,7 @@ Total membrance current across components of the cell.
 
 *  Sample value: ``cable_sample_range``. Each value is the current in
    nanoamperes across an unbranched component of the cell, as determined
-   by the discretization.
+   by the discretisation.
 
 *  Metadata: ``mcable_list``. Each cable in the cable list describes
    the unbranched component for the corresponding sample value.
@@ -582,7 +582,7 @@ Ionic external concentration of ion across components of the cell.
 
 *  Sample value: ``cable_sample_range``. Each value is the concentration in
    millimoles per lire across an unbranched component of the cell, as determined
-   by the discretization.
+   by the discretisation.
 
 *  Metadata: ``mcable_list``. Each cable in the cable list describes
    the unbranched component for the corresponding sample value.
@@ -612,7 +612,7 @@ Ionic external concentration of ion across components of the cell.
 
 *  Sample value: ``cable_sample_range``. Each value is the concentration in
    millimoles per lire across an unbranched component of the cell, as determined
-   by the discretization.
+   by the discretisation.
 
 *  Metadata: ``mcable_list``. Each cable in the cable list describes
    the unbranched component for the corresponding sample value.
@@ -646,11 +646,11 @@ If the mechanism is not defined at a particular site, that site is ignored.
         std::string state;
     };
 
-Value of state variable in adensity mechanism across components of the cell.
+Value of state variable in a density mechanism across components of the cell.
 
 *  Sample value: ``cable_sample_range``. State variable values from the
    mechanism across unbranched components of the cell, as determined
-   by the discretization and mechanism extent.
+   by the discretisation and mechanism extent.
 
 *  Metadata: ``mcable_list``. Each cable in the cable list describes
    the unbranched component for the corresponding sample value.
@@ -691,17 +691,12 @@ with which it is associated.
 
 .. _cv-policies:
 
-Discretization and CV policies
+Discretisation and CV policies
 ------------------------------
 
-For the purpose of simulation, cable cells are decomposed into discrete
-subcomponents called *control volumes* (CVs), following the finite volume method
-terminology. Each control volume comprises a connected subset of the
-morphology. Each fork point in the morphology will be the responsibility of
-a single CV, and as a special case a zero-volume CV can be used to represent
-a single fork point in isolation.
-
-The CVs are uniquely determined by a set of *B* of ``mlocation`` boundary points.
+For the purpose of simulation, cable cells are decomposed into :ref:`discrete
+subcomponents <cable-discretisation>` called *control volumes* (CVs) The CVs are
+uniquely determined by a set of *B* of ``mlocation`` boundary points.
 For each non-terminal point *h* in *B*, there is a CV comprising the points
 {*x*: *h* ≤ *x* and ¬∃ *y* ∈ *B* s.t *h* < *y* < *x*}, where < and ≤ refer to the
 geometrical partial order of locations on the morphology. A fork point is
@@ -727,7 +722,7 @@ Specific CV policy objects are created by functions described below (strictly
 speaking, these are class constructors for classes are implicit converted to
 ``cv_policy`` objects). These all take a ``region`` parameter that restrict the
 domain of applicability of that policy; this facility is useful for specifying
-differing discretizations on different parts of a cell morphology. When a CV
+differing discretisations on different parts of a cell morphology. When a CV
 policy is constrained in this manner, the boundary of the domain will always
 constitute part of the CV boundary point set.
 
@@ -765,7 +760,7 @@ supplied domain.
 
    cv_policy_every_sample(region domain = reg::all())
 
-Use every sample point in the morpholgy definition as a CV boundary, optionally
+Use every sample point in the morphology definition as a CV boundary, optionally
 restricted to the supplied domain. Each fork point in the domain is
 represented by a trivial CV.
 
diff --git a/doc/cpp_common.rst b/doc/cpp_cell.rst
similarity index 59%
rename from doc/cpp_common.rst
rename to doc/cpp_cell.rst
index 0604b98746e75562a5d7f9e92b735c4698816bbd..b49f5245b6b0fe575b23e5ed78f5b9412e87e2a4 100644
--- a/doc/cpp_common.rst
+++ b/doc/cpp_cell.rst
@@ -1,11 +1,11 @@
-.. _cppcommon:
+.. _cppcell:
 
-Common Types
+Cells
 ============
 
 .. cpp:namespace:: arb
 
-Cell Identifiers and Indexes
+Cell identifiers and indexes
 ----------------------------
 
 These types, defined in ``common_types.hpp``, are used as identifiers for
@@ -91,61 +91,3 @@ cells and members of cell-local collections.
 
         Proxy cell used for benchmarking.
 
-Probes
-------
-
-.. cpp:type:: probe_tag = int
-
-    Extra contextual information associated with a probe.
-
-.. cpp:class:: probe_info
-
-    Probes are specified in the recipe objects that are used to initialize a
-    model; the specification of the item or value that is subjected to a
-    probe will be specific to a particular cell type.
-
-    .. cpp:member:: cell_member_type id
-
-           Cell gid, index of probe.
-
-    .. cpp:member:: probe_tag tag
-
-           Opaque key, returned in sample record.
-
-    .. cpp:member:: std::any address
-
-           Cell-type specific location info, specific to cell kind of ``id.gid``.
-
-Utility Wrappers and Containers
---------------------------------
-
-.. cpp:namespace:: arb::util
-
-.. cpp:class:: unique_any
-
-   Equivalent to :cpp:class:`std::any`, except that:
-
-      * it can store any type that is move constructable;
-      * it is move only, that is, it can't be copied.
-
-.. cpp:class:: any_ptr
-
-   Holds a pointer to an arbitrary type, together with the type information.
-
-   .. cpp:function:: template <typename T> T as()
-
-      Retrieve the pointer as type T. If T is ``void *`` or the same
-      as the type of the pointer stored in ``any_ptr``, return the held
-      value, cast accordingly. Otherwise return ``nullptr``.
-
-   ``any_ptr`` can be used with ``util::any_cast``, so that
-   ``util::any_cast<T>(p)`` is equivalent to ``p.as<T>()`` for a value ``p``
-   of type ``any_ptr``.
-
-.. cpp:function:: template <typename T> any_cast(...)
-
-    Equivalent to ``std::any_cast`` for ``std::any`` arguments, ``any_cast``
-    also performs analagous casting for the :cpp:class:`unique_any` and
-    :cpp:class:`any_ptr` utility classes.
-
-
diff --git a/doc/cpp_distributed_context.rst b/doc/cpp_distributed_context.rst
index 69cef0f75e7a8c57689b4c35691f8cc1a7cb0ee9..5c49a2ebc617a6e52fd72a8cdc3a82387d21d0b1 100644
--- a/doc/cpp_distributed_context.rst
+++ b/doc/cpp_distributed_context.rst
@@ -1,6 +1,6 @@
 .. _cppdistcontext:
 
-Distributed Context
+Distributed context
 ===================
 
 To support running on systems from laptops and workstations to large distributed
@@ -69,7 +69,7 @@ A distributed context can then be generated using helper functions :cpp:func:`ar
         auto dist_ctx = arb::make_mpi_context(MPI_COMM_WORLD);
 
 
-Class Documentation
+Class documentation
 -------------------
 
 .. cpp:namespace:: arb
diff --git a/doc/cpp_domdec.rst b/doc/cpp_domdec.rst
index 736e4d6871077854a0a273f664fdb7e8bd4bbef8..3ceddbd3fc836c2db02e3f9ecf5bcd503198db8d 100644
--- a/doc/cpp_domdec.rst
+++ b/doc/cpp_domdec.rst
@@ -2,12 +2,12 @@
 
 .. cpp:namespace:: arb
 
-Domain Decomposition
+Domain decomposition
 ====================
 
 The C++ API for partitioning a model over distributed and local hardware is described here.
 
-Load Balancers
+Load balancers
 --------------
 
 Load balancing generates a :cpp:class:`domain_decomposition` given an :cpp:class:`arb::recipe`
diff --git a/doc/cpp_dry_run.rst b/doc/cpp_dry_run.rst
index 08b8c7f48b74e3e6b14c8ecc7821033d7cd5ff4d..1a557ec01adc1c9000178c3e75790e1df6aaec02 100644
--- a/doc/cpp_dry_run.rst
+++ b/doc/cpp_dry_run.rst
@@ -3,7 +3,7 @@
 .. Note::
     This is a developer feature for benchmarking, and is not useful for scientific use cases.
 
-Dry-run Mode
+Dry-run mode
 ===================
 
 Dry-run mode is used to mimic the performance of running an MPI distributed simulation
diff --git a/doc/cpp_hardware.rst b/doc/cpp_hardware.rst
index 683e65400f3bc7ff988b0067e5007c7d73eddfe1..3686a83f042c2c1a7b0326556cdf3de424b7ea3b 100644
--- a/doc/cpp_hardware.rst
+++ b/doc/cpp_hardware.rst
@@ -1,6 +1,6 @@
 .. _cpphardware:
 
-Hardware Management
+Hardware management
 ===================
 
 Arbor provides two library APIs for working with hardware resources:
@@ -75,7 +75,7 @@ own code for managing MPI, GPUs, and thread counts.
 
 .. cpp:function:: int default_gpu()
 
-   Returns the integer identifier of the first available GPU, if a GPU is available 
+   Returns the integer identifier of the first available GPU, if a GPU is available
 
    Return value:
 
@@ -223,7 +223,7 @@ The core Arbor library *libarbor* provides an API for:
             // 4 threads and the first available GPU
             arb::proc_allocation resources(8, 0);
 
-            // Construct with 
+            // Construct with
             auto num_threads = arbenv::thread_concurrency();
             auto gpu_id = arbenv::default_gpu();
             arb::proc_allocation resources(num_threads, gpu_id);
diff --git a/doc/cpp_interconnectivity.rst b/doc/cpp_interconnectivity.rst
new file mode 100644
index 0000000000000000000000000000000000000000..266708a7503ea3a52da30ba40da2487f2f4af6be
--- /dev/null
+++ b/doc/cpp_interconnectivity.rst
@@ -0,0 +1,52 @@
+.. _cppinterconnectivity:
+
+Interconnectivity
+#################
+
+.. cpp:class:: cell_connection
+
+    Describes a connection between two cells: a pre-synaptic source and a
+    post-synaptic destination. The source is typically a threshold detector on
+    a cell or a spike source. The destination is a synapse on the post-synaptic cell.
+
+    .. cpp:type:: cell_connection_endpoint = cell_member_type
+
+        Connection end-points are represented by pairs
+        (cell index, source/target index on cell).
+
+    .. cpp:member:: cell_connection_endpoint source
+
+        Source end point.
+
+    .. cpp:member:: cell_connection_endpoint dest
+
+        Destination end point.
+
+    .. cpp:member:: float weight
+
+        The weight delivered to the target synapse.
+        The weight is dimensionless, and its interpretation is
+        specific to the synapse type of the target. For example,
+        the `expsyn` synapse interprets it as a conductance
+        with units μS (micro-Siemens).
+
+    .. cpp:member:: float delay
+
+        Delay of the connection (milliseconds).
+
+.. cpp:class:: gap_junction_connection
+
+    Describes a gap junction between two gap junction sites.
+    Gap junction sites are represented by :cpp:type:cell_member_type.
+
+    .. cpp:member:: cell_member_type local
+
+        gap junction site: one half of the gap junction connection.
+
+    .. cpp:member:: cell_member_type peer
+
+        gap junction site: other half of the gap junction connection.
+
+    .. cpp:member:: float ggap
+
+        gap junction conductance in μS.
diff --git a/doc/cpp_intro.rst b/doc/cpp_overview.rst
similarity index 61%
rename from doc/cpp_intro.rst
rename to doc/cpp_overview.rst
index 14c41eeacc8fda6a67ab6b251f682be11ac37950..78072d448013adfa1d71e012941e47be76c05f21 100644
--- a/doc/cpp_intro.rst
+++ b/doc/cpp_overview.rst
@@ -3,9 +3,8 @@
 Overview
 =========
 
-The C++ API for is the main interface through which application developers will
-access Arbor, though it is designed to be usable for power users to
-implement models.
+The C++ API is the recommended interface through which advanced users and HPC
+developers can access Arbor.
 
 Arbor makes a distinction between the **description** of a model, and the
 **execution** of a model.
diff --git a/doc/profiler.rst b/doc/cpp_profiler.rst
similarity index 98%
rename from doc/profiler.rst
rename to doc/cpp_profiler.rst
index 7b11adde8613221c48de7e7e2a38f17a6194abdc..df39c429115fdf77faf512598c6e923529609404 100644
--- a/doc/profiler.rst
+++ b/doc/cpp_profiler.rst
@@ -1,3 +1,5 @@
+.. _cppprofiler:
+
 Profiler
 ========
 
@@ -16,7 +18,7 @@ For example to compile a debug build with profiling turned on:
 
     cmake .. -DARB_WITH_PROFILING=ON
 
-Instrumenting Code
+Instrumenting code
 ------------------
 
 Developers manually instrument the regions to profile.
@@ -25,7 +27,7 @@ the appropriate granularity for profiling different regions.
 
 Once a region of code is marked for the profiler, each thread in the application will track the total time spent in the region, and how many times the region is executed on that thread.
 
-Marking Regions
+Marking regions
 ~~~~~~~~~~~~~~~
 
 To instrument a region, use ``PE`` (profiler enter) and ``PL`` (profiler leave) macros to mark the beginning and end of a region.
@@ -68,7 +70,7 @@ and the difference accumulated.
 If a region includes time executing other tasks, for example when calling
 ``arb::threading::parallel_for``, the time spent executing the other tasks will be included, which will give meaningless timings.
 
-Organising Regions
+Organising regions
 ~~~~~~~~~~~~~~~~~~
 
 The profiler allows the user to build a hierarchy of regions by grouping related regions together.
@@ -152,7 +154,7 @@ hierarchical report that shows accumulated time spent in each region and its chi
 For _p_ more information on interpreting the profiler's output see
 `Running the Profiler`_ and `Profiler Output`_.
 
-Running the Profiler
+Running the profiler
 --------------------
 
 The profiler does not need to be started or stopped by the user.
@@ -194,7 +196,7 @@ and the profiler regions can be reset.
 After a call to ``util::profiler_clear``, all counters and timers are set to zero.
 This could be used, for example, to generate separate profiler reports for model building and model execution phases.
 
-Profiler Output
+Profiler output
 ~~~~~~~~~~~~~~~
 
 The profiler keeps accumulated call count and time values for each region in each thread.
diff --git a/doc/cpp_recipe.rst b/doc/cpp_recipe.rst
index 300830e03472cc5348d509c00e4e624fc732b344..7dab88765bd5630a43de81c0bba0dbf46bf28bd0 100644
--- a/doc/cpp_recipe.rst
+++ b/doc/cpp_recipe.rst
@@ -7,7 +7,7 @@ The :cpp:class:`arb::recipe` class documentation is below.
 
 .. _cpp_recipe_best_practice:
 
-C++ Best Practices
+C++ best practices
 ------------------
 
 Here we collect rules of thumb to keep in mind when making recipes in C++.
@@ -20,8 +20,8 @@ Here we collect rules of thumb to keep in mind when making recipes in C++.
     lazy evaluation when possible (see `Be lazy <_recipe_lazy>`_).
 
 
-Class Documentation
--------------------
+Recipe
+------
 
 .. cpp:namespace:: arb
 
@@ -112,26 +112,21 @@ Class Documentation
 
         By default returns 0.
 
-    .. cpp:function:: virtual cell_size_type num_probes(cell_gid_type gid) const
-
-        The number of probes attached to the cell.
-
-        By default returns 0.
-
     .. cpp:function:: virtual cell_size_type num_gap_junction_sites(cell_gid_type gid) const
 
         Returns the number of gap junction sites on `gid`.
 
         By default returns 0.
 
-    .. cpp:function:: virtual probe_info get_probe(cell_member_type) const
+    .. cpp:function:: virtual std::vector<probe_info> get_probes(cell_gid_type gid) const
 
         Intended for use by cell group implementations to set up sampling data
         structures ahead of time and for putting in place any structures or
         information in the concrete cell implementations to allow monitoring.
 
-        By default throws :cpp:type:`std::logic_error`. If :cpp:func:`num_probes`
-        returns a non-zero value, this must also be overridden.
+        Returns a vector containing (in order) all the probes on a given cell `gid`.
+
+        By default throws :cpp:type:`std::logic_error`.
 
     .. cpp:function:: virtual std::any get_global_properties(cell_kind) const
 
@@ -139,50 +134,40 @@ Class Documentation
 
         By default returns an empty container.
 
-.. cpp:class:: cell_connection
-
-    Describes a connection between two cells: a pre-synaptic source and a
-    post-synaptic destination. The source is typically a threshold detector on
-    a cell or a spike source. The destination is a synapse on the post-synaptic cell.
-
-    .. cpp:type:: cell_connection_endpoint = cell_member_type
-
-        Connection end-points are represented by pairs
-        (cell index, source/target index on cell).
-
-    .. cpp:member:: cell_connection_endpoint source
+Cells
+--------
 
-        Source end point.
+See :ref:`cppcell`.
 
-    .. cpp:member:: cell_connection_endpoint dest
+Synapses
+--------
 
-        Destination end point.
+See :ref:`cppsynapses`.
 
-    .. cpp:member:: float weight
+Probes
+------
 
-        The weight delivered to the target synapse.
-        The weight is dimensionless, and its interpretation is
-        specific to the synapse type of the target. For example,
-        the `expsyn` synapse interprets it as a conductance
-        with units μS (micro-Siemens).
+.. cpp:type:: probe_tag = int
 
-    .. cpp:member:: float delay
+    Extra contextual information associated with a probe.
 
-        Delay of the connection (milliseconds).
+.. cpp:class:: probe_info
 
-.. cpp:class:: gap_junction_connection
+    Probes are specified in the recipe objects that are used to initialize a
+    model; the specification of the item or value that is subjected to a
+    probe will be specific to a particular cell type.
 
-    Describes a gap junction between two gap junction sites.
-    Gap junction sites are represented by :cpp:type:cell_member_type.
+    .. cpp:member:: probe_tag tag
 
-    .. cpp:member:: cell_member_type local
+           Opaque key, returned in sample record.
 
-        gap junction site: one half of the gap junction connection.
+    .. cpp:member:: util::any address
 
-    .. cpp:member:: cell_member_type peer
+           Cell-type specific location info, specific to cell kind of ``id.gid``.
 
-        gap junction site: other half of the gap junction connection.
+Event generator and schedules
+-----------------------------
 
-    .. cpp:member:: float ggap
 
-        gap junction conductance in μS.
+Example
+-------
diff --git a/doc/cpp_simulation.rst b/doc/cpp_simulation.rst
index d82df56a2edb588ba852c4457c94ee497515d31d..79154352decc70edfb6fa2c75f75b212bdae6805 100644
--- a/doc/cpp_simulation.rst
+++ b/doc/cpp_simulation.rst
@@ -39,7 +39,7 @@ then build the simulation.
         arb::simulation sim(recipe, decomp, context);
 
 
-Class Documentation
+Class documentation
 -------------------
 
 .. cpp:namespace:: arb
diff --git a/doc/gs_other_examples.rst b/doc/gs_other_examples.rst
new file mode 100644
index 0000000000000000000000000000000000000000..139ce2807327d5f47c57ab26fe7d060dcd828093
--- /dev/null
+++ b/doc/gs_other_examples.rst
@@ -0,0 +1,10 @@
+.. _gs_other_examples:
+
+Other examples
+================================
+
+.. Note::
+    You can find some examples of full Arbor simulations in the ``python/examples`` directory of the `Arbor repository <https://github.com/arbor-sim/arbor>`_.
+
+.. Todo::
+    Add more in-depth tutorial-like pages building up examples here.
diff --git a/doc/gs_single_cell.rst b/doc/gs_single_cell.rst
new file mode 100644
index 0000000000000000000000000000000000000000..8749e3843009ed6ffe9e24859e2afe73d480c301
--- /dev/null
+++ b/doc/gs_single_cell.rst
@@ -0,0 +1,168 @@
+.. _gs_single_cell:
+
+A 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.
+
+This guide will walk through a series of single cell models of increasing complexity.
+Links are provided to separate documentation that covers relevant topics in more detail.
+
+In an interactive Python interpreter, you can use ``help()`` on any class or function to
+obtain some documentation. E.g.: ``help(arbor.gap_junction_connection)`` will print
+:class:`this<arbor._arbor.gap_junction_connection>`.
+
+.. _single_soma:
+
+Single compartment cell with HH dynamics
+----------------------------------------------------
+
+The most trivial representation of a cell in Arbor is to model the entire cell as a
+cylinder. The following example shows the steps required to construct a model of a
+cylindrical cell with radius 3 μm, Hodgkin–Huxley dynamics and a current clamp stimulus,
+then run the model for 100 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, spike detectors and electrical properties.
+
+Our "single-compartment HH cell" has a simple morphology and dynamics, so the steps to
+create the ``cable_cell`` that represents it are as follows:
+
+.. code-block:: python
+
+    import arbor
+
+    # (1) Create a morphology with a single (cylindrical) segment of length=diameter=6 μm
+    tree = arbor.segment_tree()
+    tree.append(arbor.mnpos, arbor.mpoint(-3, 0, 0, 3), arbor.mpoint(3, 0, 0, 3), tag=1)
+
+    # (2) Define the soma and its center
+    labels = arbor.label_dict({'soma':   '(tag 1)',
+                               'center': '(location 0 0.5)'})
+
+    # (3) Create cell and set properties
+    cell = arbor.cable_cell(tree, labels)
+    cell.set_properties(Vm=-40)
+    cell.paint('soma', 'hh')
+    cell.place('center', arbor.iclamp( 10, 2, 0.8))
+    cell.place('center', arbor.spike_detector(-10))
+
+    # Discretization: the default discretization in Arbor is 1 compartment per branch.
+    # Let's be a bit more precise and make that every 2 μm:
+    cell.compartments_length(2)
+
+
+Let's unpack that.
+
+Step **(1)** above shows how the cell is represented using a :class:`arbor.segment_tree`
+to which a single segment is added. Arbor's cell morphologies are constructed from a
+:ref:`segment tree<morph-segment_tree>` which is a list of segments, which are tapered
+cones with a *tag*. :meth:`arbor.segment_tree.append` takes 4 arguments, starting with
+the parent segment. The first segment added has no parent however, which is made clear by
+using :class:`arbor.mnpos`. Then two :class:`arbor.mpoint` s are supplied, the proximal
+and distal endpoints of the segment. Finally, an integer value can be supplied to tag the
+segment for future reference.
+
+In step **(2)** a dictionary of labels is created using :class:`arbor.label_dict<arbor.
+_arbor.label_dict>`. Cell builders need to refer to *regions* and *locations* on a cell
+morphology. Arbor uses a domains specific language (DSL) to describe regions and
+locations, which are given labels. We add two labels:
+
+* ``soma`` defines a *region* with ``(tag  2)``. Note that this corresponds to the
+  ``tag`` parameter that was used to define the single segment in step (1).
+* ``center`` defines a *location* at ``(location 0 0.5)``, which is the mid point ``0.5``
+  of branch ``0``, which corresponds to the center of the soma on the morphology defined in Step (1).
+
+In step **(3)** a :class:`arbor.cable_cell` is constructed by combining the segment tree
+with the named regions and locations.
+
+* "Cell-wide" properties are set through :meth:`arbor.cable_cell.set_properties`. Here,
+  the initial membrane potential everywhere on the cell is set to -40 mV.
+* Properties can also be set to a region of the cell, which Arbor calls 'painting'. This
+  is meant to convey placement is not precise: we wouldn't want to manually place ion
+  channels all over the surface of the cell. :meth:`arbor.cable_cell.paint` lets us
+  instruct Arbor to use HH dynamics on the region we've labelled soma and sort the details
+  out for us.
+* Other properties we do want to :meth:`arbor.cable_cell.place<arbor._arbor.cable_cell.place>`
+  in a precise :class:`arbor.location<arbor._arbor.location>`. We place two things:
+  an :class:`arbor.iclamp<arbor._arbor.iclamp>` with a duration of 2 ms and a current of
+  0.8 nA, starting at 10 ms. Then, add an :class:`arbor.spike_detector<arbor._arbor.spike_detector>`
+  with a threshold of -10 mV to the location we've labelled 'center'.
+* Finally, we adjust the :ref:`discretisation <cable-discretisation>` of the simulation
+  by setting the compartment length to 2 μm. By default there is one compartment per branch.
+
+Single cell network
+----------------------------------------------------
+
+Great, we have defined our cell! Now, let's move to the network. Arbor can simulate
+networks with multiple individual cells, connected together in a network. Single cell
+models do not require the full *recipe* interface used to describing such network models,
+with many unique cells, network and gap junctions. Arbor provides a :class:`arbor.
+single_cell_model<arbor._arbor.single_cell_model>` helper that wraps a cell description,
+and provides an interface for recording potentials and running the simulation.
+
+.. code-block:: python
+
+    # (4) Make single cell model.
+    m = arbor.single_cell_model(cell)
+
+    # (5) Attach voltage probe sampling at 10 kHz (every 0.1 ms).
+    m.probe('voltage', 'center', frequency=10000)
+
+    # (6) Run simulation for 100 ms of simulated activity.
+    m.run(tfinal=100)
+
+Step **(4)** instantiates the :class:`arbor.single_cell_model<arbor._arbor.single_cell_model>` with our single-compartment cell.
+
+In step **(5)** a :meth:`arbor.single_cell_model.probe()<arbor._arbor.single_cell_model.
+probe>` is 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 ('center'), and the frequency at which we want to sample (10kHz).
+
+Finally, step **(6)** starts the actual simulation for a duration of 100 ms.
+
+Results
+----------------------------------------------------
+
+Our cell and network are defined; we have ran the simulation. However, we have not looked
+at any results! We have added two things that should have recorded something, a
+spike_detector and a voltage probe. Let's see what they have produced!
+
+.. code-block:: python
+
+    # (7) Print spike times, if any.
+    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')
+
+    # (8) Plot the recorded voltages over time.
+    import pandas, seaborn # You may have to pip install these.
+    df = pandas.DataFrame({'t/ms': m.traces[0].time, 'U/mV': m.traces[0].value})
+    seaborn.relplot(data=df, kind="line", x="t/ms", y="U/mV").savefig('single_cell_model_result.svg')
+
+In step **(7)** we access :meth:`arbor.single_cell_model.spikes<arbor._arbor.
+single_cell_model.spikes>` to access the spike time. A single spike at a little over 10
+ms should be printed, which matches the stimulus we have provided in step (3).
+
+The other measurement we have is that of the potential, which we plot in step **(8)**.
+Arbor stores sampled quantities under :meth:`arbor.single_cell_model.traces<arbor._arbor.
+single_cell_model.traces>`. You should be seeing something like this:
+
+.. figure:: gen-images/single_cell_model_result.svg
+    :width: 400
+    :align: center
+
+    Plot of the potential over time for the voltage probe added in step (5).
+
+You can find the source code for this example in full at ``python/examples/single_cell_model.py``.
+
+.. Todo::
+    Add equivalent but more comprehensive recipe implementation in parallel, such that the reader learns how single_cell_model works.
diff --git a/doc/images/arbor-lines-proto-colour-notext.svg b/doc/images/arbor-lines-proto-colour-notext.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c9a65ca5cdbfaf498f0dcecf41f5c1d1f434225f
--- /dev/null
+++ b/doc/images/arbor-lines-proto-colour-notext.svg
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
+   sodipodi:docname="arbor-lines-proto-colour-notext.svg"
+   id="svg2"
+   version="1.1"
+   width="64.946602"
+   viewBox="0 0 64.946602 64.946598"
+   height="64.946602">
+  <metadata
+     id="metadata31">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs29" />
+  <sodipodi:namedview
+     lock-margins="false"
+     fit-margin-bottom="0"
+     fit-margin-right="0"
+     fit-margin-left="0"
+     fit-margin-top="0"
+     inkscape:document-rotation="0"
+     inkscape:current-layer="g24"
+     inkscape:window-maximized="1"
+     inkscape:window-y="0"
+     inkscape:window-x="66"
+     inkscape:cy="-8.4514794"
+     inkscape:cx="-36.623749"
+     inkscape:zoom="2.16"
+     showgrid="false"
+     id="namedview27"
+     inkscape:window-height="1411"
+     inkscape:window-width="2494"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0"
+     guidetolerance="10"
+     gridtolerance="10"
+     objecttolerance="10"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff" />
+  <g
+     transform="matrix(0.30075999,0,0,0.30075999,1.8045599,9.0227997)"
+     id="g24">
+    <path
+       inkscape:connector-curvature="0"
+       id="path16"
+       d="m -2.4051713e-8,104.3335 v 24.5142 L 206.25,128.8019 v -24.4226 z"
+       style="fill:#d86310;fill-opacity:1;fill-rule:evenodd" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path18"
+       d="m 123.0743,38.9667 c 1.1064,4.6408 2.1097,9.409 3.0799,14.1449 0.7283,3.5552 1.4246,6.9882 2.1221,10.3949 L 206.25,63.5761 V 39.0399 Z"
+       style="fill:#333333;fill-opacity:1;fill-rule:evenodd" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path20"
+       d="M -2.4051713e-8,38.9667 V 63.5724 L 37.5531,63.5065 c 1.8287,-5.9164 3.3501,-14.2671 4.7937,-22.6373 0.1101,-0.6384 0.2166,-1.266 0.3241,-1.9025 z"
+       style="fill:#333333;fill-opacity:1;fill-rule:evenodd" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path22"
+       d="M 80.3375,0 C 78.2692,-0.0024 76.1818,0.3203 74.154,1.062 67.6652,3.4356 64.1949,8.4858 62.0489,12.8485 57.757,21.574 56.5532,31.0568 54.9206,40.7446 c -1.6325,9.6879 -3.3679,19.3664 -5.5847,25.1972 -1.1037,2.9031 -2.3227,4.6727 -2.7814,5.1416 H -2.4051713e-8 V 95.6305 H 46.8732 c 6.6003,0 13.0434,-3.1384 17.1276,-7.2729 4.0843,-4.1345 6.4394,-8.8789 8.2709,-13.6963 3.663,-9.6348 5.2323,-20.2411 6.85,-29.8407 1.0828,-6.4257 2.2052,-10.0197 3.316,-14.1522 0.4491,0.9915 0.8599,1.4928 1.2598,2.7136 2.1351,6.5185 4.1289,15.4514 6.0132,24.6496 1.8843,9.1983 3.6817,18.6775 5.8136,26.9971 1.4269,5.5685 2.4005,10.4957 4.8871,15.2051 h 71.6894 c 9.7863,-3.0301 21.747,-4.0156 34.1492,-4.4074 V 71.1511 c -17.5031,0.2791 -36.8168,1.635 -54.0894,10.8545 -9.5421,5.0933 -17.2844,8.6996 -21.8536,9.6607 -4.5692,0.9611 -4.593,1.0775 -7.2803,-1.6828 1.3459,1.3825 -1.9011,-3.9085 -3.7317,-11.0522 -1.8305,-7.1437 -3.6144,-16.4146 -5.5426,-25.827 -1.9282,-9.4125 -3.979,-18.9559 -6.7328,-27.3633 C 104.2659,17.3336 102.0021,9.3494 92.8656,3.7482 90.3018,2.1764 87.2478,0.9101 84.0216,0.3351 82.8118,0.1195 81.5785,0.0015 80.3375,0 Z"
+       style="fill:#a53c00;fill-opacity:1;fill-rule:evenodd" />
+    <rect
+       y="-30"
+       x="-6"
+       height="215.94162"
+       width="215.94162"
+       id="rect836"
+       style="fill:none;stroke:none;stroke-width:3.32491" />
+  </g>
+</svg>
diff --git a/doc/images/arbor-lines-proto-colour.svg b/doc/images/arbor-lines-proto-colour.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1ab6d4096a592b2df6e6c76e6b676dc606022391
--- /dev/null
+++ b/doc/images/arbor-lines-proto-colour.svg
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   height="40"
+   viewBox="0 0 211 39.999998"
+   width="211"
+   version="1.1"
+   id="svg2"
+   sodipodi:docname="arbor-lines-proto-colour.svg"
+   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
+  <metadata
+     id="metadata31">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs29" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1955"
+     inkscape:window-height="1134"
+     id="namedview27"
+     showgrid="false"
+     inkscape:zoom="2.5867405"
+     inkscape:cx="86.405744"
+     inkscape:cy="37.524145"
+     inkscape:window-x="556"
+     inkscape:window-y="303"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="g24"
+     inkscape:document-rotation="0" />
+  <g
+     id="g24"
+     transform="matrix(0.30075999,0,0,0.30075999,0,-0.4650523)">
+    <g
+       id="g14">
+      <g
+         id="g12">
+        <path
+           style="fill:#000000"
+           d="m 297.309,43.887 v 4.0312 c -6.4922,-4.3086 -14.2305,-6.8437 -23.961,-6.8437 -23.582,0 -43.3984,19.8125 -43.3984,45.8711 0,25.8867 19.8164,46.0508 43.3984,46.0508 9.7305,0 17.4688,-2.6954 23.961,-7.0196 v 5.1446 h 29.168 V 43.887 Z m -18.0117,64.2695 c -11.8868,0 -20.711,-9.1601 -20.711,-21.2109 0,-12.0313 8.8242,-21.1914 20.711,-21.1914 6.3007,0 12.4297,1.6132 18.0117,7.7226 v 26.9531 c -5.582,6.0977 -11.711,7.7266 -18.0117,7.7266 z"
+           id="path2"
+           inkscape:connector-curvature="0" />
+        <path
+           style="fill:#000000"
+           d="m 405.8988,42.012 c -12.957,0 -22.3242,4.1601 -30.0742,10.8437 V 43.887 h -29.3594 v 87.2344 h 29.3594 V 80.7932 c 6.8438,-6.6992 15.8555,-10.4961 30.0742,-11.039 z"
+           id="path4"
+           inkscape:connector-curvature="0" />
+        <path
+           style="fill:#000000"
+           d="m 473.6957,42.012 c -9.5391,0 -17.2773,2.5351 -23.7695,6.6679 V 3.5549 h -29.3594 v 127.5665 h 29.3594 v -4.7774 c 6.4922,4.1328 14.2304,6.6524 23.7695,6.6524 23.5977,0 43.4141,-19.8008 43.4141,-45.5665 0,-25.6015 -19.8164,-45.4179 -43.4141,-45.4179 z m -5.7578,66.4961 c -6.3047,0 -12.6055,-1.586 -18.0117,-7.4336 V 74.137 c 5.4062,-5.8477 11.707,-7.4453 18.0117,-7.4453 11.8867,0 20.5312,9.0429 20.5312,20.7382 0,12.0469 -8.6445,21.0782 -20.5312,21.0782 z"
+           id="path6"
+           inkscape:connector-curvature="0" />
+        <path
+           style="fill:#000000"
+           d="m 577.2582,41.0745 c -27.7305,0 -48.0859,19.6367 -48.0859,45.8711 0,26.6015 20.3554,46.0508 48.0859,46.0508 27.5547,0 47.9102,-19.4493 47.9102,-46.0508 0,-26.2344 -20.3555,-45.8711 -47.9102,-45.8711 z m 0,67.2578 c -12.2539,0 -20.3594,-8.9844 -20.3594,-21.1953 0,-11.8555 8.1055,-21.3828 20.3594,-21.3828 12.0625,0 20.168,9.5273 20.168,21.3828 0,12.2109 -8.1055,21.1953 -20.168,21.1953 z"
+           id="path8"
+           inkscape:connector-curvature="0" />
+        <path
+           style="fill:#000000"
+           d="m 698.5551,42.012 c -12.9571,0 -22.3242,4.1601 -30.0781,10.8437 V 43.887 h -29.3555 v 87.2344 H 668.477 V 80.7932 c 6.8476,-6.6992 15.8593,-10.4961 30.0781,-11.039 z"
+           id="path10"
+           inkscape:connector-curvature="0" />
+      </g>
+    </g>
+    <path
+       style="fill:#d86310;fill-rule:evenodd"
+       d="m 0,104.3335 v 24.5142 l 206.25,-0.0458 v -24.4226 z"
+       id="path16"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#333333;fill-rule:evenodd;fill-opacity:1"
+       d="m 123.0743,38.9667 c 1.1064,4.6408 2.1097,9.409 3.0799,14.1449 0.7283,3.5552 1.4246,6.9882 2.1221,10.3949 L 206.25,63.5761 V 39.0399 Z"
+       id="path18"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#333333;fill-rule:evenodd"
+       d="m 0,38.9667 v 24.6057 l 37.5531,-0.0659 c 1.8287,-5.9164 3.3501,-14.2671 4.7937,-22.6373 0.1101,-0.6384 0.2166,-1.266 0.3241,-1.9025 z"
+       id="path20"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#a53c00;fill-rule:evenodd"
+       d="M 80.3375,0 C 78.2692,-0.0024 76.1818,0.3203 74.154,1.062 67.6652,3.4356 64.1949,8.4858 62.0489,12.8485 57.757,21.574 56.5532,31.0568 54.9206,40.7446 c -1.6325,9.6879 -3.3679,19.3664 -5.5847,25.1972 -1.1037,2.9031 -2.3227,4.6727 -2.7814,5.1416 H 0 v 24.5471 h 46.8732 c 6.6003,0 13.0434,-3.1384 17.1276,-7.2729 4.0843,-4.1345 6.4394,-8.8789 8.2709,-13.6963 3.663,-9.6348 5.2323,-20.2411 6.85,-29.8407 1.0828,-6.4257 2.2052,-10.0197 3.316,-14.1522 0.4491,0.9915 0.8599,1.4928 1.2598,2.7136 2.1351,6.5185 4.1289,15.4514 6.0132,24.6496 1.8843,9.1983 3.6817,18.6775 5.8136,26.9971 1.4269,5.5685 2.4005,10.4957 4.8871,15.2051 h 71.6894 c 9.7863,-3.0301 21.747,-4.0156 34.1492,-4.4074 V 71.1511 c -17.5031,0.2791 -36.8168,1.635 -54.0894,10.8545 -9.5421,5.0933 -17.2844,8.6996 -21.8536,9.6607 -4.5692,0.9611 -4.593,1.0775 -7.2803,-1.6828 1.3459,1.3825 -1.9011,-3.9085 -3.7317,-11.0522 -1.8305,-7.1437 -3.6144,-16.4146 -5.5426,-25.827 -1.9282,-9.4125 -3.979,-18.9559 -6.7328,-27.3633 C 104.2659,17.3336 102.0021,9.3494 92.8656,3.7482 90.3018,2.1764 87.2478,0.9101 84.0216,0.3351 82.8118,0.1195 81.5785,0.0015 80.3375,0 Z"
+       id="path22"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>
diff --git a/doc/images/arbor-logo.svg b/doc/images/arbor-logo.svg
deleted file mode 100644
index 0cab7af5cebc0fde87b048a7255d53b4805a3ed8..0000000000000000000000000000000000000000
--- a/doc/images/arbor-logo.svg
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Created with sK1 2.0rc3 (http://sk1project.net/) -->
-<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" height="133.46145" viewBox="0 0 697.610161417 133.46145" width="697.610161417" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" id="svg2" xmlns:dc="http://purl.org/dc/elements/1.1/">
-
-<g>
-	<g>
-		<g>
-			<path style="fill:#000000;" d="M 297.309,43.887 L 297.309,47.9182 C 290.8168,43.6096 283.0785,41.0745 273.348,41.0745 249.766,41.0745 229.9496,60.887 229.9496,86.9456 229.9496,112.8323 249.766,132.9964 273.348,132.9964 283.0785,132.9964 290.8168,130.301 297.309,125.9768 L 297.309,131.1214 326.477,131.1214 326.477,43.887 Z M 279.2973,108.1565 C 267.4105,108.1565 258.5863,98.9964 258.5863,86.9456 258.5863,74.9143 267.4105,65.7542 279.2973,65.7542 285.598,65.7542 291.727,67.3674 297.309,73.4768 L 297.309,100.4299 C 291.727,106.5276 285.598,108.1565 279.2973,108.1565 Z" />
-			<path style="fill:#000000;" d="M 405.8988,42.012 C 392.9418,42.012 383.5746,46.1721 375.8246,52.8557 L 375.8246,43.887 346.4652,43.887 346.4652,131.1214 375.8246,131.1214 375.8246,80.7932 C 382.6684,74.094 391.6801,70.2971 405.8988,69.7542 Z" />
-			<path style="fill:#000000;" d="M 473.6957,42.012 C 464.1566,42.012 456.4184,44.5471 449.9262,48.6799 L 449.9262,3.5549 420.5668,3.5549 420.5668,131.1214 449.9262,131.1214 449.9262,126.344 C 456.4184,130.4768 464.1566,132.9964 473.6957,132.9964 497.2934,132.9964 517.1098,113.1956 517.1098,87.4299 517.1098,61.8284 497.2934,42.012 473.6957,42.012 Z M 467.9379,108.5081 C 461.6332,108.5081 455.3324,106.9221 449.9262,101.0745 L 449.9262,74.137 C 455.3324,68.2893 461.6332,66.6917 467.9379,66.6917 479.8246,66.6917 488.4691,75.7346 488.4691,87.4299 488.4691,99.4768 479.8246,108.5081 467.9379,108.5081 Z" />
-			<path style="fill:#000000;" d="M 577.2582,41.0745 C 549.5277,41.0745 529.1723,60.7112 529.1723,86.9456 529.1723,113.5471 549.5277,132.9964 577.2582,132.9964 604.8129,132.9964 625.1684,113.5471 625.1684,86.9456 625.1684,60.7112 604.8129,41.0745 577.2582,41.0745 Z M 577.2582,108.3323 C 565.0043,108.3323 556.8988,99.3479 556.8988,87.137 556.8988,75.2815 565.0043,65.7542 577.2582,65.7542 589.3207,65.7542 597.4262,75.2815 597.4262,87.137 597.4262,99.3479 589.3207,108.3323 577.2582,108.3323 Z" />
-			<path style="fill:#000000;" d="M 698.5551,42.012 C 685.598,42.012 676.2309,46.1721 668.477,52.8557 L 668.477,43.887 639.1215,43.887 639.1215,131.1214 668.477,131.1214 668.477,80.7932 C 675.3246,74.094 684.3363,70.2971 698.5551,69.7542 Z" />
-		</g>
-	</g>
-	<path style="fill-rule:evenodd;fill:#d86310;" d="M -0.0,104.3335 L -0.0,128.8477 206.25,128.8019 206.25,104.3793 -0.0,104.3335 Z" />
-	<path style="fill-rule:evenodd;fill:#000000;" d="M 123.0743,38.9667 C 124.1807,43.6075 125.184,48.3757 126.1542,53.1116 126.8825,56.6668 127.5788,60.0998 128.2763,63.5065 L 206.25,63.5761 206.25,39.0399 123.0743,38.9667 Z" />
-	<path style="fill-rule:evenodd;fill:#000000;" d="M -0.0,38.9667 L -0.0,63.5724 37.5531,63.5065 C 39.3818,57.5901 40.9032,49.2394 42.3468,40.8692 42.4569,40.2308 42.5634,39.6032 42.6709,38.9667 L -0.0,38.9667 Z" />
-	<path style="fill-rule:evenodd;fill:#a53c00;" d="M 80.3375,0.0 C 78.2692,-0.0024 76.1818,0.3203 74.154,1.062 67.6652,3.4356 64.1949,8.4858 62.0489,12.8485 57.757,21.574 56.5532,31.0568 54.9206,40.7446 53.2881,50.4325 51.5527,60.111 49.3359,65.9418 48.2322,68.8449 47.0132,70.6145 46.5545,71.0834 L -0.0,71.0834 -0.0,95.6305 46.8732,95.6305 C 53.4735,95.6305 59.9166,92.4921 64.0008,88.3576 68.0851,84.2231 70.4402,79.4787 72.2717,74.6613 75.9347,65.0265 77.504,54.4202 79.1217,44.8206 80.2045,38.3949 81.3269,34.8009 82.4377,30.6684 82.8868,31.6599 83.2976,32.1612 83.6975,33.382 85.8326,39.9005 87.8264,48.8334 89.7107,58.0316 91.595,67.2299 93.3924,76.7091 95.5243,85.0287 96.9512,90.5972 97.9248,95.5244 100.4114,100.2338 L 172.1008,100.2338 C 181.8871,97.2037 193.8478,96.2182 206.25,95.8264 L 206.25,71.1511 C 188.7469,71.4302 169.4332,72.7861 152.1606,82.0056 142.6185,87.0989 134.8762,90.7052 130.307,91.6663 125.7378,92.6274 125.714,92.7438 123.0267,89.9835 124.3726,91.366 121.1256,86.075 119.295,78.9313 117.4645,71.7876 115.6806,62.5167 113.7524,53.1043 111.8242,43.6918 109.7734,34.1484 107.0196,25.741 104.2659,17.3336 102.0021,9.3494 92.8656,3.7482 90.3018,2.1764 87.2478,0.9101 84.0216,0.3351 82.8118,0.1195 81.5785,0.0015 80.3375,0.0 Z" />
-</g>
-</svg>
\ No newline at end of file
diff --git a/doc/images/single_cell_model_result.svg b/doc/images/single_cell_model_result.svg
new file mode 100644
index 0000000000000000000000000000000000000000..920a073097c256abab0e69b15f2560eb0312a329
--- /dev/null
+++ b/doc/images/single_cell_model_result.svg
@@ -0,0 +1,614 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Created with matplotlib (https://matplotlib.org/) -->
+<svg height="366.07625pt" version="1.1" viewBox="0 0 366.432812 366.07625" width="366.432812pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+  <style type="text/css">
+*{stroke-linecap:butt;stroke-linejoin:round;}
+  </style>
+ </defs>
+ <g id="figure_1">
+  <g id="patch_1">
+   <path d="M 0 366.07625 
+L 366.432812 366.07625 
+L 366.432812 0 
+L 0 0 
+z
+" style="fill:#ffffff;"/>
+  </g>
+  <g id="axes_1">
+   <g id="patch_2">
+    <path d="M 48.982813 328.52 
+L 359.232812 328.52 
+L 359.232812 7.2 
+L 48.982813 7.2 
+z
+" style="fill:#ffffff;"/>
+   </g>
+   <g id="matplotlib.axis_1">
+    <g id="xtick_1">
+     <g id="line2d_1">
+      <defs>
+       <path d="M 0 0 
+L 0 3.5 
+" id="m49bb8fd34e" style="stroke:#000000;stroke-width:0.8;"/>
+      </defs>
+      <g>
+       <use style="stroke:#000000;stroke-width:0.8;" x="63.085085" xlink:href="#m49bb8fd34e" y="328.52"/>
+      </g>
+     </g>
+     <g id="text_1">
+      <!-- 0 -->
+      <defs>
+       <path d="M 31.78125 66.40625 
+Q 24.171875 66.40625 20.328125 58.90625 
+Q 16.5 51.421875 16.5 36.375 
+Q 16.5 21.390625 20.328125 13.890625 
+Q 24.171875 6.390625 31.78125 6.390625 
+Q 39.453125 6.390625 43.28125 13.890625 
+Q 47.125 21.390625 47.125 36.375 
+Q 47.125 51.421875 43.28125 58.90625 
+Q 39.453125 66.40625 31.78125 66.40625 
+z
+M 31.78125 74.21875 
+Q 44.046875 74.21875 50.515625 64.515625 
+Q 56.984375 54.828125 56.984375 36.375 
+Q 56.984375 17.96875 50.515625 8.265625 
+Q 44.046875 -1.421875 31.78125 -1.421875 
+Q 19.53125 -1.421875 13.0625 8.265625 
+Q 6.59375 17.96875 6.59375 36.375 
+Q 6.59375 54.828125 13.0625 64.515625 
+Q 19.53125 74.21875 31.78125 74.21875 
+z
+" id="DejaVuSans-48"/>
+      </defs>
+      <g transform="translate(59.903835 343.118437)scale(0.1 -0.1)">
+       <use xlink:href="#DejaVuSans-48"/>
+      </g>
+     </g>
+    </g>
+    <g id="xtick_2">
+     <g id="line2d_2">
+      <g>
+       <use style="stroke:#000000;stroke-width:0.8;" x="119.564776" xlink:href="#m49bb8fd34e" y="328.52"/>
+      </g>
+     </g>
+     <g id="text_2">
+      <!-- 20 -->
+      <defs>
+       <path d="M 19.1875 8.296875 
+L 53.609375 8.296875 
+L 53.609375 0 
+L 7.328125 0 
+L 7.328125 8.296875 
+Q 12.9375 14.109375 22.625 23.890625 
+Q 32.328125 33.6875 34.8125 36.53125 
+Q 39.546875 41.84375 41.421875 45.53125 
+Q 43.3125 49.21875 43.3125 52.78125 
+Q 43.3125 58.59375 39.234375 62.25 
+Q 35.15625 65.921875 28.609375 65.921875 
+Q 23.96875 65.921875 18.8125 64.3125 
+Q 13.671875 62.703125 7.8125 59.421875 
+L 7.8125 69.390625 
+Q 13.765625 71.78125 18.9375 73 
+Q 24.125 74.21875 28.421875 74.21875 
+Q 39.75 74.21875 46.484375 68.546875 
+Q 53.21875 62.890625 53.21875 53.421875 
+Q 53.21875 48.921875 51.53125 44.890625 
+Q 49.859375 40.875 45.40625 35.40625 
+Q 44.1875 33.984375 37.640625 27.21875 
+Q 31.109375 20.453125 19.1875 8.296875 
+z
+" id="DejaVuSans-50"/>
+      </defs>
+      <g transform="translate(113.202276 343.118437)scale(0.1 -0.1)">
+       <use xlink:href="#DejaVuSans-50"/>
+       <use x="63.623047" xlink:href="#DejaVuSans-48"/>
+      </g>
+     </g>
+    </g>
+    <g id="xtick_3">
+     <g id="line2d_3">
+      <g>
+       <use style="stroke:#000000;stroke-width:0.8;" x="176.044466" xlink:href="#m49bb8fd34e" y="328.52"/>
+      </g>
+     </g>
+     <g id="text_3">
+      <!-- 40 -->
+      <defs>
+       <path d="M 37.796875 64.3125 
+L 12.890625 25.390625 
+L 37.796875 25.390625 
+z
+M 35.203125 72.90625 
+L 47.609375 72.90625 
+L 47.609375 25.390625 
+L 58.015625 25.390625 
+L 58.015625 17.1875 
+L 47.609375 17.1875 
+L 47.609375 0 
+L 37.796875 0 
+L 37.796875 17.1875 
+L 4.890625 17.1875 
+L 4.890625 26.703125 
+z
+" id="DejaVuSans-52"/>
+      </defs>
+      <g transform="translate(169.681966 343.118437)scale(0.1 -0.1)">
+       <use xlink:href="#DejaVuSans-52"/>
+       <use x="63.623047" xlink:href="#DejaVuSans-48"/>
+      </g>
+     </g>
+    </g>
+    <g id="xtick_4">
+     <g id="line2d_4">
+      <g>
+       <use style="stroke:#000000;stroke-width:0.8;" x="232.524157" xlink:href="#m49bb8fd34e" y="328.52"/>
+      </g>
+     </g>
+     <g id="text_4">
+      <!-- 60 -->
+      <defs>
+       <path d="M 33.015625 40.375 
+Q 26.375 40.375 22.484375 35.828125 
+Q 18.609375 31.296875 18.609375 23.390625 
+Q 18.609375 15.53125 22.484375 10.953125 
+Q 26.375 6.390625 33.015625 6.390625 
+Q 39.65625 6.390625 43.53125 10.953125 
+Q 47.40625 15.53125 47.40625 23.390625 
+Q 47.40625 31.296875 43.53125 35.828125 
+Q 39.65625 40.375 33.015625 40.375 
+z
+M 52.59375 71.296875 
+L 52.59375 62.3125 
+Q 48.875 64.0625 45.09375 64.984375 
+Q 41.3125 65.921875 37.59375 65.921875 
+Q 27.828125 65.921875 22.671875 59.328125 
+Q 17.53125 52.734375 16.796875 39.40625 
+Q 19.671875 43.65625 24.015625 45.921875 
+Q 28.375 48.1875 33.59375 48.1875 
+Q 44.578125 48.1875 50.953125 41.515625 
+Q 57.328125 34.859375 57.328125 23.390625 
+Q 57.328125 12.15625 50.6875 5.359375 
+Q 44.046875 -1.421875 33.015625 -1.421875 
+Q 20.359375 -1.421875 13.671875 8.265625 
+Q 6.984375 17.96875 6.984375 36.375 
+Q 6.984375 53.65625 15.1875 63.9375 
+Q 23.390625 74.21875 37.203125 74.21875 
+Q 40.921875 74.21875 44.703125 73.484375 
+Q 48.484375 72.75 52.59375 71.296875 
+z
+" id="DejaVuSans-54"/>
+      </defs>
+      <g transform="translate(226.161657 343.118437)scale(0.1 -0.1)">
+       <use xlink:href="#DejaVuSans-54"/>
+       <use x="63.623047" xlink:href="#DejaVuSans-48"/>
+      </g>
+     </g>
+    </g>
+    <g id="xtick_5">
+     <g id="line2d_5">
+      <g>
+       <use style="stroke:#000000;stroke-width:0.8;" x="289.003847" xlink:href="#m49bb8fd34e" y="328.52"/>
+      </g>
+     </g>
+     <g id="text_5">
+      <!-- 80 -->
+      <defs>
+       <path d="M 31.78125 34.625 
+Q 24.75 34.625 20.71875 30.859375 
+Q 16.703125 27.09375 16.703125 20.515625 
+Q 16.703125 13.921875 20.71875 10.15625 
+Q 24.75 6.390625 31.78125 6.390625 
+Q 38.8125 6.390625 42.859375 10.171875 
+Q 46.921875 13.96875 46.921875 20.515625 
+Q 46.921875 27.09375 42.890625 30.859375 
+Q 38.875 34.625 31.78125 34.625 
+z
+M 21.921875 38.8125 
+Q 15.578125 40.375 12.03125 44.71875 
+Q 8.5 49.078125 8.5 55.328125 
+Q 8.5 64.0625 14.71875 69.140625 
+Q 20.953125 74.21875 31.78125 74.21875 
+Q 42.671875 74.21875 48.875 69.140625 
+Q 55.078125 64.0625 55.078125 55.328125 
+Q 55.078125 49.078125 51.53125 44.71875 
+Q 48 40.375 41.703125 38.8125 
+Q 48.828125 37.15625 52.796875 32.3125 
+Q 56.78125 27.484375 56.78125 20.515625 
+Q 56.78125 9.90625 50.3125 4.234375 
+Q 43.84375 -1.421875 31.78125 -1.421875 
+Q 19.734375 -1.421875 13.25 4.234375 
+Q 6.78125 9.90625 6.78125 20.515625 
+Q 6.78125 27.484375 10.78125 32.3125 
+Q 14.796875 37.15625 21.921875 38.8125 
+z
+M 18.3125 54.390625 
+Q 18.3125 48.734375 21.84375 45.5625 
+Q 25.390625 42.390625 31.78125 42.390625 
+Q 38.140625 42.390625 41.71875 45.5625 
+Q 45.3125 48.734375 45.3125 54.390625 
+Q 45.3125 60.0625 41.71875 63.234375 
+Q 38.140625 66.40625 31.78125 66.40625 
+Q 25.390625 66.40625 21.84375 63.234375 
+Q 18.3125 60.0625 18.3125 54.390625 
+z
+" id="DejaVuSans-56"/>
+      </defs>
+      <g transform="translate(282.641347 343.118437)scale(0.1 -0.1)">
+       <use xlink:href="#DejaVuSans-56"/>
+       <use x="63.623047" xlink:href="#DejaVuSans-48"/>
+      </g>
+     </g>
+    </g>
+    <g id="xtick_6">
+     <g id="line2d_6">
+      <g>
+       <use style="stroke:#000000;stroke-width:0.8;" x="345.483538" xlink:href="#m49bb8fd34e" y="328.52"/>
+      </g>
+     </g>
+     <g id="text_6">
+      <!-- 100 -->
+      <defs>
+       <path d="M 12.40625 8.296875 
+L 28.515625 8.296875 
+L 28.515625 63.921875 
+L 10.984375 60.40625 
+L 10.984375 69.390625 
+L 28.421875 72.90625 
+L 38.28125 72.90625 
+L 38.28125 8.296875 
+L 54.390625 8.296875 
+L 54.390625 0 
+L 12.40625 0 
+z
+" id="DejaVuSans-49"/>
+      </defs>
+      <g transform="translate(335.939788 343.118437)scale(0.1 -0.1)">
+       <use xlink:href="#DejaVuSans-49"/>
+       <use x="63.623047" xlink:href="#DejaVuSans-48"/>
+       <use x="127.246094" xlink:href="#DejaVuSans-48"/>
+      </g>
+     </g>
+    </g>
+    <g id="text_7">
+     <!-- t/ms -->
+     <defs>
+      <path d="M 18.3125 70.21875 
+L 18.3125 54.6875 
+L 36.8125 54.6875 
+L 36.8125 47.703125 
+L 18.3125 47.703125 
+L 18.3125 18.015625 
+Q 18.3125 11.328125 20.140625 9.421875 
+Q 21.96875 7.515625 27.59375 7.515625 
+L 36.8125 7.515625 
+L 36.8125 0 
+L 27.59375 0 
+Q 17.1875 0 13.234375 3.875 
+Q 9.28125 7.765625 9.28125 18.015625 
+L 9.28125 47.703125 
+L 2.6875 47.703125 
+L 2.6875 54.6875 
+L 9.28125 54.6875 
+L 9.28125 70.21875 
+z
+" id="DejaVuSans-116"/>
+      <path d="M 25.390625 72.90625 
+L 33.6875 72.90625 
+L 8.296875 -9.28125 
+L 0 -9.28125 
+z
+" id="DejaVuSans-47"/>
+      <path d="M 52 44.1875 
+Q 55.375 50.25 60.0625 53.125 
+Q 64.75 56 71.09375 56 
+Q 79.640625 56 84.28125 50.015625 
+Q 88.921875 44.046875 88.921875 33.015625 
+L 88.921875 0 
+L 79.890625 0 
+L 79.890625 32.71875 
+Q 79.890625 40.578125 77.09375 44.375 
+Q 74.3125 48.1875 68.609375 48.1875 
+Q 61.625 48.1875 57.5625 43.546875 
+Q 53.515625 38.921875 53.515625 30.90625 
+L 53.515625 0 
+L 44.484375 0 
+L 44.484375 32.71875 
+Q 44.484375 40.625 41.703125 44.40625 
+Q 38.921875 48.1875 33.109375 48.1875 
+Q 26.21875 48.1875 22.15625 43.53125 
+Q 18.109375 38.875 18.109375 30.90625 
+L 18.109375 0 
+L 9.078125 0 
+L 9.078125 54.6875 
+L 18.109375 54.6875 
+L 18.109375 46.1875 
+Q 21.1875 51.21875 25.484375 53.609375 
+Q 29.78125 56 35.6875 56 
+Q 41.65625 56 45.828125 52.96875 
+Q 50 49.953125 52 44.1875 
+z
+" id="DejaVuSans-109"/>
+      <path d="M 44.28125 53.078125 
+L 44.28125 44.578125 
+Q 40.484375 46.53125 36.375 47.5 
+Q 32.28125 48.484375 27.875 48.484375 
+Q 21.1875 48.484375 17.84375 46.4375 
+Q 14.5 44.390625 14.5 40.28125 
+Q 14.5 37.15625 16.890625 35.375 
+Q 19.28125 33.59375 26.515625 31.984375 
+L 29.59375 31.296875 
+Q 39.15625 29.25 43.1875 25.515625 
+Q 47.21875 21.78125 47.21875 15.09375 
+Q 47.21875 7.46875 41.1875 3.015625 
+Q 35.15625 -1.421875 24.609375 -1.421875 
+Q 20.21875 -1.421875 15.453125 -0.5625 
+Q 10.6875 0.296875 5.421875 2 
+L 5.421875 11.28125 
+Q 10.40625 8.6875 15.234375 7.390625 
+Q 20.0625 6.109375 24.8125 6.109375 
+Q 31.15625 6.109375 34.5625 8.28125 
+Q 37.984375 10.453125 37.984375 14.40625 
+Q 37.984375 18.0625 35.515625 20.015625 
+Q 33.0625 21.96875 24.703125 23.78125 
+L 21.578125 24.515625 
+Q 13.234375 26.265625 9.515625 29.90625 
+Q 5.8125 33.546875 5.8125 39.890625 
+Q 5.8125 47.609375 11.28125 51.796875 
+Q 16.75 56 26.8125 56 
+Q 31.78125 56 36.171875 55.265625 
+Q 40.578125 54.546875 44.28125 53.078125 
+z
+" id="DejaVuSans-115"/>
+     </defs>
+     <g transform="translate(192.988281 356.796562)scale(0.1 -0.1)">
+      <use xlink:href="#DejaVuSans-116"/>
+      <use x="39.208984" xlink:href="#DejaVuSans-47"/>
+      <use x="72.900391" xlink:href="#DejaVuSans-109"/>
+      <use x="170.3125" xlink:href="#DejaVuSans-115"/>
+     </g>
+    </g>
+   </g>
+   <g id="matplotlib.axis_2">
+    <g id="ytick_1">
+     <g id="line2d_7">
+      <defs>
+       <path d="M 0 0 
+L -3.5 0 
+" id="m52d6302f9e" style="stroke:#000000;stroke-width:0.8;"/>
+      </defs>
+      <g>
+       <use style="stroke:#000000;stroke-width:0.8;" x="48.982813" xlink:href="#m52d6302f9e" y="321.183759"/>
+      </g>
+     </g>
+     <g id="text_8">
+      <!-- −80 -->
+      <defs>
+       <path d="M 10.59375 35.5 
+L 73.1875 35.5 
+L 73.1875 27.203125 
+L 10.59375 27.203125 
+z
+" id="DejaVuSans-8722"/>
+      </defs>
+      <g transform="translate(20.878125 324.982978)scale(0.1 -0.1)">
+       <use xlink:href="#DejaVuSans-8722"/>
+       <use x="83.789062" xlink:href="#DejaVuSans-56"/>
+       <use x="147.412109" xlink:href="#DejaVuSans-48"/>
+      </g>
+     </g>
+    </g>
+    <g id="ytick_2">
+     <g id="line2d_8">
+      <g>
+       <use style="stroke:#000000;stroke-width:0.8;" x="48.982813" xlink:href="#m52d6302f9e" y="280.896601"/>
+      </g>
+     </g>
+     <g id="text_9">
+      <!-- −60 -->
+      <g transform="translate(20.878125 284.695819)scale(0.1 -0.1)">
+       <use xlink:href="#DejaVuSans-8722"/>
+       <use x="83.789062" xlink:href="#DejaVuSans-54"/>
+       <use x="147.412109" xlink:href="#DejaVuSans-48"/>
+      </g>
+     </g>
+    </g>
+    <g id="ytick_3">
+     <g id="line2d_9">
+      <g>
+       <use style="stroke:#000000;stroke-width:0.8;" x="48.982813" xlink:href="#m52d6302f9e" y="240.609442"/>
+      </g>
+     </g>
+     <g id="text_10">
+      <!-- −40 -->
+      <g transform="translate(20.878125 244.408661)scale(0.1 -0.1)">
+       <use xlink:href="#DejaVuSans-8722"/>
+       <use x="83.789062" xlink:href="#DejaVuSans-52"/>
+       <use x="147.412109" xlink:href="#DejaVuSans-48"/>
+      </g>
+     </g>
+    </g>
+    <g id="ytick_4">
+     <g id="line2d_10">
+      <g>
+       <use style="stroke:#000000;stroke-width:0.8;" x="48.982813" xlink:href="#m52d6302f9e" y="200.322284"/>
+      </g>
+     </g>
+     <g id="text_11">
+      <!-- −20 -->
+      <g transform="translate(20.878125 204.121503)scale(0.1 -0.1)">
+       <use xlink:href="#DejaVuSans-8722"/>
+       <use x="83.789062" xlink:href="#DejaVuSans-50"/>
+       <use x="147.412109" xlink:href="#DejaVuSans-48"/>
+      </g>
+     </g>
+    </g>
+    <g id="ytick_5">
+     <g id="line2d_11">
+      <g>
+       <use style="stroke:#000000;stroke-width:0.8;" x="48.982813" xlink:href="#m52d6302f9e" y="160.035126"/>
+      </g>
+     </g>
+     <g id="text_12">
+      <!-- 0 -->
+      <g transform="translate(35.620312 163.834345)scale(0.1 -0.1)">
+       <use xlink:href="#DejaVuSans-48"/>
+      </g>
+     </g>
+    </g>
+    <g id="ytick_6">
+     <g id="line2d_12">
+      <g>
+       <use style="stroke:#000000;stroke-width:0.8;" x="48.982813" xlink:href="#m52d6302f9e" y="119.747968"/>
+      </g>
+     </g>
+     <g id="text_13">
+      <!-- 20 -->
+      <g transform="translate(29.257812 123.547187)scale(0.1 -0.1)">
+       <use xlink:href="#DejaVuSans-50"/>
+       <use x="63.623047" xlink:href="#DejaVuSans-48"/>
+      </g>
+     </g>
+    </g>
+    <g id="ytick_7">
+     <g id="line2d_13">
+      <g>
+       <use style="stroke:#000000;stroke-width:0.8;" x="48.982813" xlink:href="#m52d6302f9e" y="79.46081"/>
+      </g>
+     </g>
+     <g id="text_14">
+      <!-- 40 -->
+      <g transform="translate(29.257812 83.260028)scale(0.1 -0.1)">
+       <use xlink:href="#DejaVuSans-52"/>
+       <use x="63.623047" xlink:href="#DejaVuSans-48"/>
+      </g>
+     </g>
+    </g>
+    <g id="ytick_8">
+     <g id="line2d_14">
+      <g>
+       <use style="stroke:#000000;stroke-width:0.8;" x="48.982813" xlink:href="#m52d6302f9e" y="39.173651"/>
+      </g>
+     </g>
+     <g id="text_15">
+      <!-- 60 -->
+      <g transform="translate(29.257812 42.97287)scale(0.1 -0.1)">
+       <use xlink:href="#DejaVuSans-54"/>
+       <use x="63.623047" xlink:href="#DejaVuSans-48"/>
+      </g>
+     </g>
+    </g>
+    <g id="text_16">
+     <!-- U/mV -->
+     <defs>
+      <path d="M 8.6875 72.90625 
+L 18.609375 72.90625 
+L 18.609375 28.609375 
+Q 18.609375 16.890625 22.84375 11.734375 
+Q 27.09375 6.59375 36.625 6.59375 
+Q 46.09375 6.59375 50.34375 11.734375 
+Q 54.59375 16.890625 54.59375 28.609375 
+L 54.59375 72.90625 
+L 64.5 72.90625 
+L 64.5 27.390625 
+Q 64.5 13.140625 57.4375 5.859375 
+Q 50.390625 -1.421875 36.625 -1.421875 
+Q 22.796875 -1.421875 15.734375 5.859375 
+Q 8.6875 13.140625 8.6875 27.390625 
+z
+" id="DejaVuSans-85"/>
+      <path d="M 28.609375 0 
+L 0.78125 72.90625 
+L 11.078125 72.90625 
+L 34.1875 11.53125 
+L 57.328125 72.90625 
+L 67.578125 72.90625 
+L 39.796875 0 
+z
+" id="DejaVuSans-86"/>
+     </defs>
+     <g transform="translate(14.798438 181.494375)rotate(-90)scale(0.1 -0.1)">
+      <use xlink:href="#DejaVuSans-85"/>
+      <use x="73.193359" xlink:href="#DejaVuSans-47"/>
+      <use x="106.884766" xlink:href="#DejaVuSans-109"/>
+      <use x="204.296875" xlink:href="#DejaVuSans-86"/>
+     </g>
+    </g>
+   </g>
+   <g id="line2d_15">
+    <path clip-path="url(#pea632d05c8)" d="M 63.085085 240.609442 
+L 63.367484 268.853158 
+L 63.932281 295.739397 
+L 64.426478 306.580671 
+L 64.708876 309.451131 
+L 64.991275 310.992518 
+L 65.273673 311.794635 
+L 65.556072 312.195492 
+L 65.83847 312.376785 
+L 66.473867 312.412842 
+L 67.60346 312.053077 
+L 69.58025 311.098473 
+L 71.839437 309.759731 
+L 74.381023 307.999988 
+L 77.699205 305.415768 
+L 86.171159 298.667669 
+L 89.277542 296.531845 
+L 91.254331 295.321216 
+L 92.101526 21.805455 
+L 92.383925 37.118805 
+L 96.337503 156.820674 
+L 96.9023 169.789614 
+L 97.184698 217.926161 
+L 97.749495 250.100895 
+L 98.879089 303.501927 
+L 99.161488 309.920879 
+L 99.443886 312.611457 
+L 99.726284 313.556795 
+L 100.008683 313.849482 
+L 100.57348 313.899438 
+L 101.985472 313.588824 
+L 104.24466 312.889842 
+L 106.503847 311.968071 
+L 108.763035 310.807197 
+L 111.304621 309.2223 
+L 114.128606 307.170475 
+L 118.152783 303.929575 
+L 124.083151 299.173071 
+L 127.471932 296.762489 
+L 130.295917 295.004761 
+L 133.119901 293.497215 
+L 135.943886 292.248918 
+L 138.485472 291.351521 
+L 141.027058 290.670668 
+L 143.568644 290.205351 
+L 146.392629 289.929787 
+L 149.216613 289.878824 
+L 152.605395 290.040577 
+L 168.13731 291.106575 
+L 174.632474 291.099525 
+L 197.789147 290.878751 
+L 249.468064 290.915211 
+L 345.13054 290.916218 
+L 345.13054 290.916218 
+" style="fill:none;stroke:#1f77b4;stroke-linecap:square;stroke-width:1.5;"/>
+   </g>
+   <g id="patch_3">
+    <path d="M 48.982813 328.52 
+L 48.982813 7.2 
+" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
+   </g>
+   <g id="patch_4">
+    <path d="M 48.982813 328.52 
+L 359.232812 328.52 
+" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
+   </g>
+  </g>
+ </g>
+ <defs>
+  <clipPath id="pea632d05c8">
+   <rect height="321.32" width="310.25" x="48.982813" y="7.2"/>
+  </clipPath>
+ </defs>
+</svg>
diff --git a/doc/install.rst b/doc/in_build_install.rst
similarity index 96%
rename from doc/install.rst
rename to doc/in_build_install.rst
index 46e731b68232d07c700fe8a7410be5ac2c9da87a..9aad7293aa5949f8339267c10604781e6924f122 100644
--- a/doc/install.rst
+++ b/doc/in_build_install.rst
@@ -1,24 +1,22 @@
-.. _installarbor:
+.. _in_build_install:
 
-Installing Arbor
-################
+Build and install from source
+#############################
 
-.. Note::
-    If you want to get started quickly experimenting with Arbor via its Python wrapper, see the Python installation guide at :doc:`/python`.
+This guide covers building and installing Arbor using CMake, which is the recommended method for configuring Arbor for HPC applications and developers.
 
-This guide covers installing Arbor using CMake, which is the recommended method for configuring Arbor for HPC applications and developers.
+We start with an overview of the building process, and the various options available to customize the build.
+Then installation and running on `HPC clusters <cluster_>`_ is covered, followed by a `troubleshooting guide <troubleshooting_>`_ for common build problems.
 
-This guide starts with an overview of the building process, and the various options
-available to customize the build.
-The guide then covers installation and running on `HPC clusters <cluster_>`_, followed by a
-`troubleshooting guide <troubleshooting_>`_ for common build problems.
+.. note::
+    To get help in case of problems installing Arbor, please make an issue on the Arbor `Github issues <https://github.com/arbor-sim/arbor/issues>`_ page.
 
 .. _install_requirements:
 
 Requirements
 ============
 
-Minimum Requirements
+Minimum requirements
 --------------------
 
 The non distributed (i.e. no MPI) version of Arbor can be compiled on Linux or OS X systems
@@ -102,10 +100,10 @@ We recommend using GCC or Clang, for which Arbor has been tested and optimised.
     compiler issues. We strongly recommend building with GCC or Clang instead on PowerPC
     and Intel platforms.
 
-Optional Requirements
+Optional requirements
 ---------------------
 
-GPU Support
+GPU support
 ~~~~~~~~~~~
 
 Arbor has full support for NVIDIA GPUs, for which the NVIDIA CUDA toolkit version 10 is required.
@@ -143,7 +141,7 @@ install `Sphinx <http://www.sphinx-doc.org/en/master/>`_.
 
 .. _install-downloading:
 
-Getting the Code
+Getting the code
 ================
 
 The easiest way to acquire the latest version of Arbor is to check the code out from
@@ -173,7 +171,7 @@ If you use the zip file, then don't forget to run Git submodule update manually.
 
 .. _building:
 
-Building and Installing Arbor
+Building and installing Arbor
 =============================
 
 Once the Arbor code has been checked out, first run CMake to configure the build, then run make.
@@ -213,7 +211,7 @@ This will build Arbor in release mode with the `default C++ compiler <note_CC_>`
 
 .. _quickstart:
 
-Quick Start: Examples
+Quick start: examples
 ---------------------
 
 Below are some example of CMake configurations for Arbor. For more detail on individual
@@ -267,7 +265,7 @@ CMake parameters and flags, follow links to the more detailed descriptions below
 
 .. _buildtarget:
 
-Build Target
+Build target
 ------------
 
 By default, Arbor is built in release mode, which should be used when installing
@@ -340,7 +338,7 @@ with AVX, AVX2 or AVX512 ISA extensions, and for ARM architectures with support
 
 .. _install-gpu:
 
-GPU Backend
+GPU backend
 -----------
 
 Compiling for the GPU backend is controlled by the ``ARB_GPU`` CMake option which is used to select between NVIDIA and AMD GPUs
@@ -400,7 +398,7 @@ Arbor is built for all supported AMD GPUs and the available GPU will be used at
 
 .. _install-python:
 
-Python Frontend
+Python frontend
 ----------------
 
 Arbor can be used with a python frontend which is enabled by toggling the
@@ -524,11 +522,11 @@ currently relies upon private headers that are not installed.
 
 .. _cluster:
 
-HPC Clusters
+HPC clusters
 ============
 
 HPC clusters offer their own unique challenges when compiling and running
-software, so we cover some common issues in this section.  If you have problems
+software, so we cover some common issues in this section. If you have problems
 on your target system that are not covered here, please make an issue on the
 Arbor `Github issues <https://github.com/arbor-sim/arbor/issues>`_ page.
 We will do our best to help you directly, and update this guide to help other users.
@@ -576,7 +574,7 @@ using the supplied MPI compiler wrappers in preference.
         $ CC --version
         g++ (GCC) 6.2.0 20160822 (Cray Inc.)
 
-Cray Systems
+Cray systems
 ------------
 
 The compiler used by the MPI wrappers is set using a "programming environment" module.
@@ -649,7 +647,7 @@ Troubleshooting
 
 .. _crosscompiling:
 
-Cross Compiling NMODL
+Cross compiling NMODL
 ---------------------
 
 Care must be taken when Arbor is compiled on a system with a different
@@ -800,7 +798,7 @@ and have to be turned on by setting the ``ARB_WITH_ASSERTIONS`` CMake option:
     `bug report <https://github.com/arbor-sim/arbor/issues>`_ you send to the Arbor developers!
 
 
-CMake Git Submodule Warnings
+CMake Git submodule warnings
 ----------------------------
 
 When running CMake, warnings like the following indicate that the Git submodules
diff --git a/doc/in_install.rst b/doc/in_install.rst
new file mode 100644
index 0000000000000000000000000000000000000000..02947d4270d87c36113a21959bc58e6c3a837ab6
--- /dev/null
+++ b/doc/in_install.rst
@@ -0,0 +1,12 @@
+.. _in_install:
+
+Install Arbor
+#############################
+
+Currently we offer three ways to get Arbor.
+
+1. **Python Package**. To get started quickly experimenting with Arbor via its Python API on your personal machine, see the :ref:`Python installation guide <gs_python>`.
+2. **Build and install from source**. To build and install Arbor, on your own machine or HPC environment, see :ref:`gs_build_install`.
+3. **Spack**. See :ref:`Spack installation guide <gs_spack>`.
+
+If you wish to use the C++ API, you'll need to build Arbor from source or use Spack. Note that you can build the Python bindings with both of those as well.
diff --git a/doc/python.rst b/doc/in_python.rst
similarity index 70%
rename from doc/python.rst
rename to doc/in_python.rst
index b519a94a33d6681dbdabe4602a9c0b72013fcb0c..44753679ace4deedc7ab3b1dbbd5620002e56a13 100644
--- a/doc/python.rst
+++ b/doc/in_python.rst
@@ -1,9 +1,9 @@
-.. _getstarted_python:
+.. _in_python:
 
-Python
-======
+Python Installation
+===================
 
-Arbor's Python wrapper will be the most convenient interface for most users.
+Arbor's Python API will be the most convenient interface for most users. Note that we only support Python 3.6 and later. Any instruction hereafter assumes you're using `python` and `pip` no older than that.
 
 Getting Arbor
 -------------
@@ -13,9 +13,9 @@ The easiest way to get Arbor is with
 
 .. code-block:: bash
 
-    pip3 install arbor
+    pip3 install arbor --user
 
-Every point release it pushed to the Python Package Index. If you wish to install another version, it is also possible to use Setuptools directly on a local copy of the source code, or instruct `pip` to install directly from our git repository:
+Every point release is pushed to the Python Package Index. If you wish to install another version, it is possible to use Setuptools directly on a local copy of the source code, or instruct `pip` to install directly from our git repository:
 
 .. code-block:: bash
 
@@ -24,19 +24,17 @@ Every point release it pushed to the Python Package Index. If you wish to instal
     python3 install ./arbor/setup.py
 
     # tell pip to build and install from master
-    pip install git+https://github.com/arbor-sim/arbor.git
-
-.. note::
-    You will need to have some development packages installed in order to build Arbor this way. For Debian/Ubuntu: `sudo apt install build-essential python-dev`, Fedora/Red Hat/CentOS: `sudo yum install @development-tools python-devel`.
+    pip3 install git+https://github.com/arbor-sim/arbor.git --user
 
 .. note::
-    Arbor's Setuptools process simplifies installation for common configurations
-    on laptops and workstations by calling CMake under the hood.
+    You will need to have some development packages installed in order to build Arbor this way.
 
-    To install Arbor on a HPC cluster, or to configure Arbor with system-specific
-    options, we recommend using the :ref:`CMake build process <installarbor>`.
+    * Ubuntu/Debian: `sudo apt install git build-essential python3-dev python3-pip`
+    * Fedora/CentOS/Red Hat: `sudo yum install git @development-tools python3-devel python3-pip`
+    * macOS: get `brew` `here <https://brew.sh>`_ and run `brew install cmake clang python3`
+    * Windows: the simplest way is to use `WSL <https://docs.microsoft.com/en-us/windows/wsl/install-win10>`_ and then follow the instructions for Ubuntu.
 
-To test that Arbor is available in Python, try the following in a `Python 3 <python2_>`_ interpreter
+To test that Arbor is available, try the following in a Python interpreter
 to see information about the version and enabled features:
 
 .. code-block:: python
@@ -45,7 +43,12 @@ to see information about the version and enabled features:
     >>> print(arbor.__version__)
     >>> print(arbor.__config__)
 
-Advanced Options
+You are now ready to use Arbor! You can continue reading these documentation pages, have a look at the :mod:`Python API Reference<arbor._arbor>`, or visit the :ref:`Quick Start page<gs_single_cell>`.
+
+.. Note::
+    To get help in case of problems installing with pip, run pip with the ``--verbose`` flag, and attach the output (along with the pip command itself) to a ticket on `Arbor's issues page <https://github.com/arbor-sim/arbor/issues>`_.
+
+Advanced options
 ^^^^^^^^^^^^^^^^^^
 
 By default Arbor is installed with multi-threading enabled.
@@ -116,17 +119,11 @@ below demonstrate this for both pip and ``setup.py``.
 .. Note::
     Detailed instructions on how to install using CMake are in the
     :ref:`Python configuration <install-python>` section of the
-    :ref:`installation guide <installarbor>`.
+    :ref:`installation guide <gs_install>`.
     CMake is recommended for developers, integration with package managers such as
     Spack and EasyBuild, and users who require fine grained control over compilation
     and installation.
 
-.. Note::
-    To report problems installing with pip,
-    run pip with the ``--verbose`` flag, and attach the output (along with
-    the pip command itself) to a ticket on
-    `Arbor's issues page <https://github.com/arbor-sim/arbor/issues>`_.
-
 Dependencies
 ^^^^^^^^^^^^^
 
@@ -147,20 +144,3 @@ Performance
 The Python interface can incur significant memory and runtime overheads relative to C++
 during the *model building* phase, however simulation performance is the same
 for both interfaces.
-
-.. _python2:
-
-Python 2
-----------
-
-Python 2 reached `end of life <https://pythonclock.org/>`_ in January 2020.
-Arbor only provides support for Python 3.6 and later.
-
-.. note::
-    It might be possible to install and run Arbor
-    using Python 2.7 by setting the ``PYTHON_EXECUTABLE`` variable when
-    :ref:`configuring CMake <install-python>`.
-    However, Arbor is not tested against Python 2.7, and we won't be able
-    to provide support.
-
-
diff --git a/doc/in_spack.rst b/doc/in_spack.rst
new file mode 100644
index 0000000000000000000000000000000000000000..447b411a0238934d3bbbd3dc86e8307b37bb2709
--- /dev/null
+++ b/doc/in_spack.rst
@@ -0,0 +1,11 @@
+.. _in_spack:
+
+Spack Installation
+===================
+
+`Spack <https://spack.io>`_ is a package manager for supercomputers, Linux, and macOS. It makes installing scientific software easy. Spack isn’t tied to a particular language; you can build a software stack in Python or R, link to libraries written in C, C++, or Fortran, and easily swap compilers or target specific microarchitectures.
+
+To install Arbor using Spack, run ``spack install arbor``.
+
+.. Note::
+    To get help in case of problems, please make an issue at `Arbor's issues page <https://github.com/arbor-sim/arbor/issues>`_.
diff --git a/doc/index.rst b/doc/index.rst
index e83b29dd51557e7f05766c65a7587c75456631b4..74e6158c45a9287c55405f705309f67ef3915e4d 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -4,20 +4,7 @@ Arbor
 .. image:: https://travis-ci.org/arbor-sim/arbor.svg?branch=master
     :target: https://travis-ci.org/arbor-sim/arbor
 
-Arbor is a high-performance library for computational neuroscience simulations
-with morphologically-detailed cells, from single cell models to very large networks.
-
-The development team is from HPC centers, aiming to help neuroscientists
-effectively use contemporary and future HPC systems to meet their simulation needs.
-
-Arbor is designed from the ground up for **many core**  architectures:
-
-    * Written in modern C++ and CUDA;
-    * Distributed parallelism using MPI;
-    * Multithreading with TBB and C++11 threads;
-    * **Open source** and **open development**;
-    * Sound development practices: **unit testing**, **continuous Integration**,
-      and **validation**.
+Arbor is a high-performance library for computational neuroscience simulations with multi-compartment, morphologically-detailed cells, from single cell models to very large networks. Arbor is written from the ground up with many-cpu and gpu architectures in mind, to help neuroscientists effectively use contemporary and future HPC systems to meet their simulation needs. The performance portability is by virtue of back-end specific optimizations for x86 multicore, Intel KNL, and NVIDIA GPUs. When coupled with low memory overheads, these optimizations make Arbor an order of magnitude faster than the most widely-used comparable simulation software. Arbor is open source and openly developed, and we use development practices such as unit testing, continuous integration, and validation.
 
 Citing Arbor
 ------------
@@ -37,62 +24,76 @@ Citing Arbor
 Alternative citation formats for the paper can be `downloaded here <https://ieeexplore.ieee.org/abstract/document/8671560>`_, and a preprint is available at `arXiv <https://arxiv.org/abs/1901.07454>`_.
 
 .. toctree::
-   :caption: Getting Stared:
+   :caption: Get Arbor:
 
-   install
-   python
-   single_cell
+   in_install
+   in_python
+   in_build_install
+   in_spack
 
 .. toctree::
-   :caption: Concepts:
+   :caption: Getting started:
 
-   morphology
-   labels
-   cable_cell
-   mechanisms
+   gs_single_cell
+   gs_other_examples
 
 .. toctree::
-   :caption: Arbor Models:
-
-   model_intro
-   model_concepts
-   model_hardware
-   model_recipe
-   model_domdec
-   model_simulation
+   :caption: How does Arbor work?
+
+   co_overview
+   co_recipe
+   co_cell
+   co_cable_cell
+   co_morphology
+   co_labels
+   co_mechanisms
+   co_interconnectivity
+   co_hardware
+   co_domdec
+   co_simulation
 
 .. toctree::
-   :caption: Python:
+   :caption: Python API
 
-   py_intro
-   py_common
+   py_overview
    py_recipe
+   py_cell
    py_cable_cell
+   py_morphology
+   py_labels
+   py_mechanisms
+   py_interconnectivity
    py_hardware
    py_domdec
    py_simulation
    py_profiler
+   py_reference
 
 .. toctree::
-   :caption: C++ API:
+   :caption: C++ API
 
-   cpp_intro
-   cpp_common
-   cpp_hardware
+   cpp_overview
    cpp_recipe
+   cpp_cell
+   cpp_cable_cell
+   cpp_interconnectivity
+   cpp_hardware
    cpp_domdec
    cpp_simulation
-   cpp_cable_cell
+   cpp_profiler
    cpp_neuroml
 
 .. toctree::
-   :caption: Developers:
+   :caption: C++ API for HPC
 
-   library
-   nmodl
-   simd_api
-   profiler
-   sampling_api
    cpp_distributed_context
    cpp_dry_run
 
+.. toctree::
+   :caption: Arbor Internals
+
+   ai_library
+   ai_nmodl
+   ai_simd_api
+   ai_sampling_api
+
diff --git a/doc/library.rst b/doc/library.rst
deleted file mode 100644
index f3c996a79654f91c1ffc70b83cc8192b72a713e0..0000000000000000000000000000000000000000
--- a/doc/library.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-.. _libref:
-
-Library Reference
-#################
-
-Low level library reference material goes here, e.g. `range` library documentation.
diff --git a/doc/py_cable_cell.rst b/doc/py_cable_cell.rst
index 7e783c2f45f79cc3451e673259de644cb4c28fab..baaeed5c3ff3312dc79c69921865832616de3c7b 100644
--- a/doc/py_cable_cell.rst
+++ b/doc/py_cable_cell.rst
@@ -1,6 +1,96 @@
 .. _pycable_cell:
 
-Python Cable Cells
-====================
+Cable cells
+===========
+
+.. currentmodule:: arbor
+
+.. py:class:: cable_cell
+
+    A cable cell is constructed from a :ref:`morphology <morph-morphology>`
+    and an optional :ref:`label dictionary <labels-dictionary>`.
+
+    .. note::
+        The regions and locsets defined in the label dictionary are
+        :ref:`concretised <labels-concretise>` when the cable cell is constructed,
+        and an exception will be thrown if an invalid label expression is found.
+
+        There are two reasons an expression might be invalid:
+
+        1. Explicit reference to a location of cable that does not exist in the
+           morphology, for example ``(branch 12)`` on a cell with 6 branches.
+        2. Reference to an incorrect label: circular reference, or a label that does not exist.
+
+
+    .. code-block:: Python
+
+        import arbor
+
+        # Construct the morphology from an SWC file.
+        tree = arbor.load_swc('granule.swc')
+        morph = arbor.morphology(tree, spherical_root=True)
+
+        # Define regions using standard SWC tags
+        labels = arbor.label_dict({'soma': '(tag 1)',
+                                   'axon': '(tag 2)',
+                                   'dend': '(join (tag 3) (tag 4))'})
+
+        # Construct a cable cell.
+        cell = arbor.cable_cell(morph, labels)
+
+    .. method:: set_properties(Vm=None, cm=None, rL=None, tempK=None)
+
+        Set default values of cable properties on the whole cell.
+        Overrides the default global values, and can be overridden by painting
+        the values onto regions.
+
+        :param str region: name of the region.
+        :param Vm: Initial membrane voltage [mV].
+        :type Vm: float or None
+        :param cm: Membrane capacitance [F/m²].
+        :type cm: float or None
+        :param rL: Axial resistivity of cable [Ω·cm].
+        :type rL: float or None
+        :param tempK: Temperature [Kelvin].
+        :type tempK: float or None
+
+        .. code-block:: Python
+
+            # Set cell-wide values for properties
+            cell.set_properties(Vm=-70, cm=0.01, rL=100, tempK=280)
+
+    .. method: compartments_length(length)
+
+        Adjust the :ref:`compartments length <cable-discretisation>`.
+
+        :param int length: length of compartments [μm].
+
+        Defaults to one control volume per branch.
+
+    .. method:: paint(region, [Vm=None, cm=None, rL=None, tempK=None])
+
+        Set cable properties on a region.
+
+        :param str region: name of the region.
+        :param Vm: Initial membrane voltage [mV].
+        :type Vm: float or None
+        :param cm: Membrane capacitance [F/m²].
+        :type cm: float or None
+        :param rL: Axial resistivity of cable [Ω·cm].
+        :type rL: float or None
+        :param tempK: Temperature [Kelvin].
+        :type tempK: float or None
+
+        .. code-block:: Python
+
+            # Specialize resistivity on soma
+            cell.paint('"soma"', rL=100)
+            # Specialize resistivity and capacitance on the axon, where
+            # axon is defined using a region expression.
+            cell.paint('(tag 2)', cm=0.05, rL=80)
+
+.. py:class:: ion
+
+    properties of an ionic species.
 
 
diff --git a/doc/py_cell.rst b/doc/py_cell.rst
new file mode 100644
index 0000000000000000000000000000000000000000..71a960f909bb84650c2d3c5e8ec3a0b9adac3496
--- /dev/null
+++ b/doc/py_cell.rst
@@ -0,0 +1,154 @@
+.. _pycell:
+
+Cells
+=====================
+
+Cell identifiers and indexes
+----------------------------
+The types defined below are used as identifiers for cells and members of cell-local collections.
+
+.. module:: arbor
+
+.. class:: cell_member
+
+    .. function:: cell_member(gid, index)
+
+        Construct a ``cell_member`` object with parameters :attr:`gid` and :attr:`index` for global identification of a cell-local item.
+
+        Items of type :class:`cell_member` must:
+
+        * be associated with a unique cell, identified by the member :attr:`gid`;
+        * identify an item within a cell-local collection by the member :attr:`index`.
+
+        An example is uniquely identifying a synapse in the model.
+        Each synapse has a post-synaptic cell (with :attr:`gid`), and an :attr:`index` into the set of synapses on the post-synaptic cell.
+
+        Lexographically ordered by :attr:`gid`, then :attr:`index`.
+
+    .. attribute:: gid
+
+        The global identifier of the cell.
+
+    .. attribute:: index
+
+        The cell-local index of the item.
+        Local indices for items within a particular cell-local collection should be zero-based and numbered contiguously.
+
+    An example of a cell member construction reads as follows:
+
+    .. container:: example-code
+
+        .. code-block:: python
+
+            import arbor
+
+            # construct
+            cmem = arbor.cell_member(0, 0)
+
+            # set gid and index
+            cmem.gid = 1
+            cmem.index = 42
+
+.. class:: cell_kind
+
+    Enumeration used to identify the cell kind, used by the model to group equal kinds in the same cell group.
+
+    .. attribute:: cable
+
+        A cell with morphology described by branching 1D cable segments.
+
+    .. attribute:: lif
+
+        A leaky-integrate and fire neuron.
+
+    .. attribute:: spike_source
+
+        A proxy cell that generates spikes from a spike sequence provided by the user.
+
+    .. attribute:: benchmark
+
+        A proxy cell used for benchmarking.
+
+    An example for setting the cell kind reads as follows:
+
+    .. container:: example-code
+
+        .. code-block:: python
+
+            import arbor
+
+            kind = arbor.cell_kind.cable
+
+Cell kinds
+----------
+
+.. class:: lif_cell
+
+    A benchmarking cell (leaky integrate-and-fire), used by Arbor developers to test communication performance,
+    with neuronal parameters:
+
+    .. attribute:: tau_m
+
+        Membrane potential decaying constant [ms].
+
+    .. attribute:: V_th
+
+        Firing threshold [mV].
+
+    .. attribute:: C_m
+
+        Membrane capacitance [pF].
+
+    .. attribute:: E_L
+
+        Resting potential [mV].
+
+    .. attribute:: V_m
+
+        Initial value of the Membrane potential [mV].
+
+    .. attribute:: t_ref
+
+        Refractory period [ms].
+
+    .. attribute:: V_reset
+
+        Reset potential [mV].
+
+.. class:: spike_source_cell
+
+    A spike source cell, that generates a user-defined sequence of spikes
+    that act as inputs for other cells in the network.
+
+    .. function:: spike_source_cell(schedule)
+
+        Construct a spike source cell that generates spikes
+
+        - at regular intervals (using an :class:`arbor.regular_schedule`)
+        - at a sequence of user-defined times (using an :class:`arbor.explicit_schedule`)
+        - at times defined by a Poisson sequence (using an :class:`arbor.poisson_schedule`)
+
+        :param schedule: User-defined sequence of time points (choose from :class:`arbor.regular_schedule`, :class:`arbor.explicit_schedule`, or :class:`arbor.poisson_schedule`).
+
+.. class:: benchmark_cell
+
+    A benchmarking cell, used by Arbor developers to test communication performance.
+
+    .. function:: benchmark_cell(schedule, realtime_ratio)
+
+        A benchmark cell generates spikes at a user-defined sequence of time points:
+
+        - at regular intervals (using an :class:`arbor.regular_schedule`)
+        - at a sequence of user-defined times (using an :class:`arbor.explicit_schedule`)
+        - at times defined by a Poisson sequence (using an :class:`arbor.poisson_schedule`)
+
+        and the time taken to integrate a cell can be tuned by setting the parameter ``realtime_ratio``.
+
+        :param schedule: User-defined sequence of time points (choose from :class:`arbor.regular_schedule`, :class:`arbor.explicit_schedule`, or :class:`arbor.poisson_schedule`).
+
+        :param realtime_ratio: Time taken to integrate a cell, for example if ``realtime_ratio`` = 2, a cell will take 2 seconds of CPU time to simulate 1 second.
+
+.. class:: cable_cell
+    :noindex:
+
+    See :ref:`pycable_cell`.
diff --git a/doc/py_common.rst b/doc/py_common.rst
deleted file mode 100644
index 8fc9edf9f953d7b9890de3f86e4d87bd9de06b5a..0000000000000000000000000000000000000000
--- a/doc/py_common.rst
+++ /dev/null
@@ -1,81 +0,0 @@
-.. _pycommon:
-
-Common Types
-=====================
-
-Cell Identifiers and Indexes
-----------------------------
-The types defined below are used as identifiers for cells and members of cell-local collections.
-
-.. module:: arbor
-
-.. class:: cell_member
-
-    .. function:: cell_member(gid, index)
-
-        Construct a cell member with parameters :attr:`gid` and :attr:`index` for global identification of a cell-local item.
-
-        Items of type :class:`cell_member` must:
-
-        * be associated with a unique cell, identified by the member :attr:`gid`;
-        * identify an item within a cell-local collection by the member :attr:`index`.
-
-        An example is uniquely identifying a synapse in the model.
-        Each synapse has a post-synaptic cell (with :attr:`gid`), and an :attr:`index` into the set of synapses on the post-synaptic cell.
-
-        Lexographically ordered by :attr:`gid`, then :attr:`index`.
-
-    .. attribute:: gid
-
-        The global identifier of the cell.
-
-    .. attribute:: index
-
-        The cell-local index of the item.
-        Local indices for items within a particular cell-local collection should be zero-based and numbered contiguously.
-
-    An example of a cell member construction reads as follows:
-
-    .. container:: example-code
-
-        .. code-block:: python
-
-            import arbor
-
-            # construct
-            cmem = arbor.cell_member(0, 0)
-
-            # set gid and index
-            cmem.gid = 1
-            cmem.index = 42
-
-.. class:: cell_kind
-
-    Enumeration used to identify the cell kind, used by the model to group equal kinds in the same cell group.
-
-    .. attribute:: cable
-
-        A cell with morphology described by branching 1D cable segments.
-
-    .. attribute:: lif
-
-        A leaky-integrate and fire neuron.
-
-    .. attribute:: spike_source
-
-        A proxy cell that generates spikes from a spike sequence provided by the user.
-
-    .. attribute:: benchmark
-
-        A proxy cell used for benchmarking.
-
-    An example for setting the cell kind reads as follows:
-
-    .. container:: example-code
-
-        .. code-block:: python
-
-            import arbor
-
-            kind = arbor.cell_kind.cable
-
diff --git a/doc/py_domdec.rst b/doc/py_domdec.rst
index 87cb0c6170b2164b6c5d543dc48b4825e21c836a..2f0bfaad349786a6329287b7eb7f18a93cdc4b2e 100644
--- a/doc/py_domdec.rst
+++ b/doc/py_domdec.rst
@@ -1,11 +1,11 @@
 .. _pydomdec:
 
-Domain Decomposition
+Domain decomposition
 ====================
 
 The Python API for partitioning a model over distributed and local hardware is described here.
 
-Load Balancers
+Load balancers
 --------------
 
 .. currentmodule:: arbor
diff --git a/doc/py_hardware.rst b/doc/py_hardware.rst
index 94ba7e8d6bea1ee0806086dae6a2794882d43a4e..057a290d42905e4cd0b70496f8d81383eb3b62b4 100644
--- a/doc/py_hardware.rst
+++ b/doc/py_hardware.rst
@@ -1,6 +1,6 @@
 .. _pyhardware:
 
-Hardware Management
+Hardware management
 ===================
 
 Arbor provides two ways for working with hardware resources:
@@ -8,7 +8,7 @@ Arbor provides two ways for working with hardware resources:
 * *Prescribe* the hardware resources and their contexts for use in Arbor simulations.
 * *Query* available hardware resources (e.g. the number of available GPUs), and initializing MPI.
 
-Available Resources
+Available resources
 -------------------
 
 Helper functions for checking cmake or environment variables, as well as configuring and checking MPI are the following:
@@ -59,7 +59,7 @@ Helper functions for checking cmake or environment variables, as well as configu
 
     Check if MPI is finalized.
 
-Prescribed Resources
+Prescribed resources
 ---------------------
 
 The Python wrapper provides an API for:
diff --git a/doc/py_interconnectivity.rst b/doc/py_interconnectivity.rst
new file mode 100644
index 0000000000000000000000000000000000000000..86f3d02f532dda3e1fd99531a1a3b9f216319ede
--- /dev/null
+++ b/doc/py_interconnectivity.rst
@@ -0,0 +1,67 @@
+.. _pyinterconnectivity:
+
+Interconnectivity
+#################
+
+.. class:: connection
+
+    Describes a connection between two cells, defined by source and destination end points (that is pre-synaptic and post-synaptic respectively),
+    a connection weight and a delay time.
+
+    .. function:: connection(source, destination, weight, delay)
+
+        Construct a connection between the :attr:`source` and the :attr:`dest` with a :attr:`weight` and :attr:`delay`.
+
+    .. attribute:: source
+
+        The source end point of the connection (type: :class:`arbor.cell_member`).
+
+    .. attribute:: dest
+
+        The destination end point of the connection (type: :class:`arbor.cell_member`).
+
+    .. attribute:: weight
+
+        The weight delivered to the target synapse.
+        The weight is dimensionless, and its interpretation is specific to the type of the synapse target.
+        For example, the expsyn synapse interprets it as a conductance with units μS (micro-Siemens).
+
+    .. attribute:: delay
+
+        The delay time of the connection [ms]. Must be positive.
+
+    An example of a connection reads as follows:
+
+    .. container:: example-code
+
+        .. code-block:: python
+
+            import arbor
+
+            # construct a connection between cells (0,0) and (1,0) with weight 0.01 and delay of 10 ms.
+            src  = arbor.cell_member(0,0)
+            dest = arbor.cell_member(1,0)
+            w    = 0.01
+            d    = 10
+            con  = arbor.connection(src, dest, w, d)
+
+.. class:: gap_junction_connection
+
+    Describes a gap junction between two gap junction sites.
+    Gap junction sites are represented by :class:`arbor.cell_member`.
+
+    .. function::gap_junction_connection(local, peer, ggap)
+
+        Construct a gap junction connection between :attr:`local` and :attr:`peer` with conductance :attr:`ggap`.
+
+    .. attribute:: local
+
+        The gap junction site: one half of the gap junction connection.
+
+    .. attribute:: peer
+
+        The gap junction site: other half of the gap junction connection.
+
+    .. attribute:: ggap
+
+        The gap junction conductance [μS].
diff --git a/doc/py_labels.rst b/doc/py_labels.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d4aa13c9bac142fc5ed838eb5e3743e37bcd75ad
--- /dev/null
+++ b/doc/py_labels.rst
@@ -0,0 +1,136 @@
+.. _py_labels:
+
+Cell labels
+===========
+
+The ``arbor.label_dict`` type is used for creating and manipulating label dictionaries,
+which can be initialised with a dictionary that defines (label, expression)
+pairs. For example, a dictionary that uses tags that correspond to SWC
+`structure identifiers <http://www.neuronland.org/NLMorphologyConverter/MorphologyFormats/SWC/Spec.html>`_
+to label soma, axon, dendrite and apical dendrites is:
+
+.. code-block:: python
+
+    import arbor
+
+    labels = {'soma': '(tag 1)',
+              'axon': '(tag 2)',
+              'dend': '(tag 3)',
+              'apic': '(tag 4)'}
+
+    d = arbor.label_dict(labels)
+
+The same ``label_dict`` can be created by starting with an empty label dictionary and adding the labels and their definitions one by one:
+
+.. code-block:: python
+
+    import arbor
+
+    d = arbor.label_dict()
+
+    d['soma'] = '(tag 1)'
+    d['axon'] = '(tag 2)'
+    d['dend'] = '(tag 3)'
+    d['apic'] = '(tag 4)'
+
+The square bracket operator is used above to add label definitions. It can
+be used to modify existing definitions, so long as the new new definition has the
+same type (region or locset):
+
+.. code-block:: python
+
+    import arbor
+
+    # A label dictionary that defines the label "dend" that defines a region.
+    d = arbor.label_dict({'dend': '(tag 3)')
+
+    # The definition of a label can be overwritten with a definition of the
+    # same type, in this case a region.
+    d['dend'] = '(join (tag 3) (tag 4))'
+
+    # However, a region can't be overwritten by a locset, or vice-versa.
+    d['dend'] = '(terminal)' # error: '(terminal)' defines a locset.
+
+    # New labels can be added to the dictionary.
+    d['soma'] = '(tag 1)'
+    d['axon'] = '(tag 2)'
+
+    # Square brackets can also be used to get a label's definition.
+    assert(d['soma'] == '(tag 1)')
+
+Expressions can refer to other regions and locsets in a label dictionary.
+In the example below, we define a region labeled *'tree'* that is the union
+of both the *'dend'* and *'apic'* regions.
+
+.. code-block:: python
+
+    import arbor
+
+    d = arbor.label_dict({
+            'soma': '(tag 1)',
+            'axon': '(tag 2)',
+            'dend': '(tag 3)',
+            'apic': '(tag 4)',
+            # equivalent to (join (tag 3) (tag 4))
+            'tree': '(join (region "dend") (region "apic"))'})
+
+The order that labels are defined does not matter, so an expression can refer to a
+label that has not yet been defined:
+
+.. code-block:: python
+
+    import arbor
+
+    d = arbor.label_dict()
+    # 'reg' refers
+    d['reg'] = '(distal_interval (locset "loc"))'
+    d['loc'] = '(location 3 0.5)'
+
+    # If d was applied to a morphology, 'reg' would refer to the region:
+    #   '(distal_interval (location 3 0.5))'
+    # Which is the sub-tree of the matrix starting at '(location 3 0.5)'
+
+    # The locset 'loc' can be redefined
+    d['loc'] = '(proximal (tag 3))'
+
+    # Now if d was applied to a morphology, 'reg' would refer to:
+    #   '(distal_interval (proximal (tag 3))'
+    # Which is the subtrees that start at the proximal locations of
+    # the region '(tag 3)'
+
+Cyclic dependencies are not permitted, as in the following example where
+two labels refer to one another:
+
+.. code-block:: python
+
+    import arbor
+
+    d = arbor.label_dict()
+    d['reg'] = '(distal_interval (locset "loc"))'
+    d['loc'] = '(proximal (region "reg"))'
+
+    # Error: 'reg' needs the definition of 'loc', which in turn needs the
+    # definition of 'reg'.
+
+.. note::
+    In the example above there will be no error when the label dictionary is defined.
+    Instead, there will be an error later when the label dictionary is applied to
+    a morphology, and the cyclic dependency is detected when concretising the locations
+    in the locsets and the cable segments in the regions.
+
+
+The type of an expression, locset or region, is inferred automatically when it is
+input into a label dictionary.
+Lists of the labels for regions and locsets are available as attributes:
+
+.. code-block:: python
+
+    import arbor
+
+    d = arbor.label_dict({
+            'soma': '(tag 1)',
+            'axon': '(tag 2)',
+            'dend': '(tag 3)',
+            'apic': '(tag 4)',
+            'site': '(location 2 0.5)',
+            'term': '(terminal)'})
diff --git a/doc/mechanisms.rst b/doc/py_mechanisms.rst
similarity index 56%
rename from doc/mechanisms.rst
rename to doc/py_mechanisms.rst
index 46dcf8e0964b4ad376929fcdebbca17f9ef2bce8..2fffffda403ab8d1680062f4cc55837553080bd1 100644
--- a/doc/mechanisms.rst
+++ b/doc/py_mechanisms.rst
@@ -1,251 +1,117 @@
-.. _mechanisms:
-
-Mechanisms
-===========
-
-Mechanisms describe biophysical processes such as ion channels and synapses.
-Mechanisms are assigned to regions and locations on a cell morphology
-through a process that called :ref:`decoration <cablecell-decoration>`.
-Mechanisms are described using a dialect of the :ref:`NMODL <nmodl>` domain
-specific language that is similarly used in `NEURON <https://neuron.yale.edu/neuron/>`_.
-
-Mechanism Catalogues
-----------------------
-
-A *mechanism catalogue* is a collection of mechanisms that maintains:
-
-1. Collection of mechanism metadata indexed by name.
-2. A further hierarchy of *derived* mechanisms, that allow specialization of
-   global parameters, ion bindings, and implementations.
-3. A map for looking up a concrete mechanism implementation on a target hardware back end.
-
-A derived mechanism can be given a new name. Alternatively derived mechanisms can
-be created implitlitly.
-When a mechanism name of the form ``"mech/param=value,..."`` is :ref:`requested <mechanisms-name-note>`,
-if the mechanism of that name does not already exist in the catalogue, it will be
-implicitly derived from an existing mechanism ``"mech"``, with global parameters
-and ion bindings overridden by the supplied assignments that follow the slash.
-If the mechanism in question has a single ion dependence, then that ion name
-can be omitted in the assignments; ``"mech/oldion=newion"`` will make the same
-derived mechanism as simply ``"mech/newion"``.
+.. _py_mechanisms:
 
-In additional being able to derive new mechanisms, catalogtues provide and interface
-for looking up a mechanism by name, and querying the following properties:
+Cell mechanisms
+===============
 
-* Global parameter: name, units and default value.
-* Range parameters: name, units and default value.
-* State variables: name, units and default value.
-* Ion dependency: for each ion whether it writes concentrations or reversal potential, and
-  whether the mechanism reads the reversal potential.
-
-Default Mechanisms
-''''''''''''''''''
-
-Arbor provides a default catalogue with the following mechanisms:
-
-* *pas*: Leaky current (:ref:`density mechanism <mechanisms-density>`).
-* *hh*:  Classic Hodgkin-Huxley dynamics (:ref:`density mechanism <mechanisms-density>`).
-* *nernst*: Calculate reversal potential for an ionic species using the Nernst equation (:ref:`reversal potential mechanism <mechanisms-revpot>`)
-* *expsyn*: Synapse with discontinuous change in conductance at an event followed by an exponential decay (:ref:`point mechanism <mechanisms-point>`).
-* *exp2syn*: Two state kinetic scheme synapse described by two time constants: rise and decay (:ref:`point mechanism <mechanisms-point>`).
-
-With the exception of *nernst*, these mechanisms are the same as those available in NEURON.
-
-Parameters
-''''''''''
-
-Mechanism behavior can be tuned using parameters and ion channel dependencies,
-prescribed in the NMODL description.
-Parameters and ion species are set initially before a simulation starts, and remain
-unchanged thereafter, for the duration of the simulation.
-There are two types of parameters that can be set by users:
-
-* *Global* parameters are a single scalar value that is the same everywhere a mechanism is defined.
-* *Range* parameters can vary spatially.
-
-Every mechanism is described with a *mechanism description*, a
-``(name, range_parameters)`` tuple, where ``name`` is a string,
-and ``range_parameters`` is an optional dictionary of key-value pairs
-that specifies values for range parameters.
-For example, consider a mechanism that models passive leaky dynamics with
-the following parameters:
-
-* *Name*: ``"passive"``.
-* *Global parameter*: reversal potential ``el``, default -65 mV.
-* *Range parameter*: conductance ``g``, default 0.001 S⋅cm⁻².
-
-The following example mechanism descriptions for our passive mechanism show that parameters and
-ion species dependencies only need to be specified when they differ from their defaults:
-
-* ``("passive")``: the passive mechanism with default parameters.
-* ``("passive/el=-80")``: derive a new passive mechanism with a non-default value for global parameter.
-* ``("passive", {"gl": 0.005})``: passive mechanism with a new a non-default range parameter value.
-* ``("passive/el=-80", {"gl": 0.005})``: derive a new passive mechanism that overrides both 
-
-Similarly to global parameters, ion species can be renamed in the mechanism name.
-This allows the use of generic mechanisms that can be adapted to a specific species
-during model instantiation.
-For example, the ``nernst`` mechanism in Arbor's default mechanism catalogue calculates
-the reversal potential of a generic ionic species ``x`` according to its internal
-and external concentrations and valence. To specialize ``nersnt`` for calcium name it
-``("nernst/x=ca")``, or if there is only one ions species in the mechanism the following
-shorthand ``("nernst/ca")`` can be used unambiguously.
-
-.. _mechanisms-name-note:
-
-.. note::
-    Global parameter values and ionic dependencies are the same for each instance of
-    a mechanism, so when these are redifeind a new mechanism is created, derived from
-    the parent mechanism.
-    For this reason, new global parameter values and ion renaming are part of the name of
-    the new mechanism, or a mechanism with a new unique name must be defined.
-
-
-Mechanism Types
----------------
-
-There are two broad categories of mechanism, density mechanisms and
-point mechanisms, and a third special density mechanism for
-computing ionic reversal potentials.
-
-.. _mechanisms-density:
-
-Density mechanisms
-''''''''''''''''''''''
-
-Density mechanisms are :ref:`NMODL mechanisms <nmodl>`
-which describe biophysical processes that are distributed in space, but whose behaviour
-is defined purely by the state of the cell and the process at any given point.
-
-Density mechanisms are commonly used to describe ion channel dynamics,
-for example the ``hh`` and ``pas`` mechanisms provided by NEURON and Arbor,
-which model classic Hodgkin-Huxley and passive leaky currents respectively.
-
-.. _mechanisms-revpot:
-
-Ion reversal potential mechanisms
-'''''''''''''''''''''''''''''''''
-
-These mechanisms, which describe ionic reversal potential
-behaviour, can be specified for cells or the whole model.
-
-The reversal potential of an ion species is calculated by an
-optional *reversal potential mechanism*.
-If no such mechanism is specified for an ion species, the initial
-reversal potential values are maintained for the course of a simulation.
-Otherwise, the mechanism does the work.
-
-Reversal potential mechanisms are density mechanisms subject to some strict restrictions.
-Specifically, a reversal potential mechanism described in NMODL:
+When :ref:`decorating <cablecell-decoration>` a cable cell, we use a :py:class:`mechanism` type to describe a
+mechanism that is to be painted or placed on the cable cell.
 
-* May not maintain any state variables.
-* Can only write to the reversal potential (``eX``) value of the ion species.
-* Can not be a :ref:`point mechanism <mechanisms-point>`.
+.. py:class:: mechanism
 
-Essentially, reversal potential mechanisms must be pure functions of cellular
-and ionic state.
+    Mechanisms describe physical processes, distributed over the membrane of the cell.
+    *Density mechanisms* are associated with regions of the cell, whose dynamics are
+    a function of the cell state and their own state where they are present.
+    *Point mechanisms* are defined at discrete locations on the cell, which receive
+    events from the network.
+    A third, specific type of density mechanism, which describes ionic reversal potential
+    behaviour, can be specified for cells or the whole model.
 
-.. note::
-    Arbor imposes greater restrictions on mechanisms that update ionic reversal potentials
-    than NEURON. Doing so simplifies reasoning about interactions between
-    mechanisms that share ionic species, by virtue of having one mechanism, and one
-    mechanism only, that calculates reversal potentials according to concentrations
-    that the other mechanisms use and modify.
+    The :class:`mechanism` type is a simple wrapper around a mechanism
+    :attr:`mechanism.name` and a dictionary of named parameters.
 
-.. _mechanisms-point:
+    Mechanisms have two types of parameters:
 
-Point mechanisms
-'''''''''''''''''''''''''''''''''
+    * global parameters: a scalar value that is the same for all instances
+      of a mechanism.
+    * range parameters: the value of range parameters is defined for each instance
+      of the mechanism on a cell. For density mechanisms, this means one value for
+      each compartment on which it is present.
 
-*Point mechanisms*, which are associated with connection end points on a
-cable cell, are placed at discrete locations on the cell.
-Unlike density mechanisms, whose behaviour is defined purely by the state of the cell and the process,
-their behavior is additionally governed by the timing and weight of events delivered
-via incoming connections.
+    The method for setting a parameter depends on its type.
+    If global parameters change, we are effectively defining a new type
+    of mechanism, so global parameter information is encoded in the
+    name.
+    Range parameters are set using a dictionary of name-value pairs.
 
-Python API
-----------
+    .. code-block:: Python
 
-Mechanism Catalogues
-''''''''''''''''''''
+        import arbor
 
-.. py:class:: catalogue
+        # hh dynamics with default parameters.
+        hh = arbor.mechanism('hh')
 
-    A *mechanism catalogue* is a collection of mechanisms that maintains:
+        # A passive leaky channel with custom parameters
+        pas = arbor.mechanism('pas', {'e': -55, 'gl': 0.02})
 
-    1. Collection of mechanism metadata indexed by name.
-    2. A further hierarchy of *derived* mechanisms, that allow specialization of
-       global parameters, ion bindings, and implementations.
+        # Reversal potential using Nernst equation with GLOBAL parameter values
+        # for Faraday's constant and the target ion species, set with a '/' followed
+        # by comma-separated list of parameter after the base mechanism name.
+        rev = arbor.mechanism('nernst/F=96485,x=ca')
 
-    .. py:method:: has(name)
+    .. method:: mechanism(name, params)
 
-        Test if mechanism with *name* is in the catalogue.
+        constructor for mechanism with *name* and range parameter overrides *params*,
+        for example: ``arbor.mechanism(name='pas', params={'g': 0.01})``.
 
         :param name: name of mechanism.
         :type name: str
-        :return: bool
-
-    .. py:method:: is_derived(name)
+        :param params: A dictionary of parameter values, with parameter name as key.
+        :type params: dict[str, double]
 
-        Is *name* a derived mechanism or can it be implicitly derived?
+    .. method:: mechanism(name)
+        :noindex:
 
-        :param name: name of mechanism.
-        :type name: str
-        :return: bool
+        constructor for mechanism.
+        The *name* can be either the name of a mechanism in the catalogue,
+        e.g.  ``arbor.mechanism('pas')``, or an implicitly derived mechanism,
+        e.g. ``arbor.mechanism('nernst/k')``.
 
-    .. py:method:: __getitem__(name)
+    .. method:: set(name, value)
 
-        Look up mechanism meta data with *name*.
+        Set new value for a parameter.
 
-        .. code-block:: Python
+        :param name: name of the parameter.
+        :type name: str
+        :param value: value of the parameter.
+        :type value: float
 
-            import arbor
+    .. py:attribute:: name
+        :type: str
 
-            cat = arbor.default_catalogue()
+        The name of the mechanism.
 
-            # Print default value and units for gnabar parameter of hh.
-            print(cat['hh'].parameters['gnabar'])
+    .. py:attribute:: values
+        :type: dict
 
-        :param name: name of mechanism.
-        :type name: str
-        :return: mechanism metadata
-        :rtype: :class:`mechanism_info`
+        A dictionary of key-value pairs for the parameters.
 
-    .. py:method:: derive(name, parent, globals={}, ions={})
+    .. code-block:: Python
 
-        Derive a new mechanism with *name* from the mechanism *parent*.
+        import arbor
 
-        If no parameters or ion renaming are specified with *globals* or *ions*,
-        the method will attempt to implicitly derive a new mechanism from parent by parsing global and
-        ions from the parent string.
+        # Create pas mechanism with default parameter values (set in NOMDL file).
+        m1 = arbor.mechanism('passive')
 
-        .. code-block:: Python
+        # Create default mechainsm with custom conductance (range).
+        m2 = arbor.mechanism('passive', {'g', 0.1})
 
-            import arbor
+        # Create a new pas mechanism with that changes reversal potential (global).
+        m3 = arbor.mechanism('passive/el=-45')
 
-            cat = arbor.default_catalogue()
+        # Create an instance of the same mechanism, that also sets conductance (range).
+        m4 = arbor.mechanism('passive/el=-45', {'g', 0.1})
 
-            # Use the value of the Faraday constant as published by CODATA in 1986,
-            # and bind to pottasium ion species.
-            cat.derive('krev',  'nernst', globals={'F': 96485.309}, ions={'x': 'k'})
+        # This is an equivalent to m4, using set method to specify range parameters.
+        m5 = arbor.mechanism('passive/el=-45')
+        m5.set('g', 0.1)
 
-            # Derive a reversal potential mechanism for sodium from the one we defined
-            # for potasium, which will inherit the redefined Faraday constant.
-            cat.derive('narev', 'krev', ions={'k': 'na'})
+        # Decorate the 'soma' on a cable_cell.
 
-            # Alternatively, we can derive a mechanism with global parameters and ion renaming
-            # specified in the parent name string.
-            cat.derive('krev_imp', 'nernst/F=96485.309,k')
-            cat.derive('carev', 'krev_imp/ca')
+        cell.paint('"soma"', m1)
+        cell.paint('"soma"', m2) # Error: can't place the same mechanism on overlapping regions
+        cell.paint('"soma"', m3) # This would be ok: m3 is a new, derived mechanism by virtue of
+                                 # having a different name, i.e. 'passive/el=-45' vs. 'passive'.
 
-        :param name: name of new derived mechanism.
-        :type name: str
-        :param parent: name of parent mechanism.
-        :type parent: str
-        :param globals: a dictionary mapping global parameter names to their values, if any.
-        :type globals: dict[str, float]
-        :param ions: a dictionary renaming ion species, if any.
-        :type ions: dict[str, str]
 
 .. py:class:: mechanism_info
 
@@ -368,112 +234,84 @@ Mechanism Catalogues
 
 The :py:class:`mechanism_info` type above presents read-only information about a mechanism that is available in a catalogue.
 
-When :ref:`decorating <cablecell-decoration>` a cable cell, we use a :py:class:`mechanism` type to describe a
-mechanism that is to be painted or placed on the cable cell.
 
-.. py:class:: mechanism
-
-    Mechanisms describe physical processes, distributed over the membrane of the cell.
-    *Density mechanisms* are associated with regions of the cell, whose dynamics are
-    a function of the cell state and their own state where they are present.
-    *Point mechanisms* are defined at discrete locations on the cell, which receive
-    events from the network.
-    A third, specific type of density mechanism, which describes ionic reversal potential
-    behaviour, can be specified for cells or the whole model.
-
-    The :class:`mechanism` type is a simple wrapper around a mechanism
-    :attr:`mechanism.name` and a dictionary of named parameters.
-
-    Mechanisms have two types of parameters:
-
-    * global parameters: a scalar value that is the same for all instances
-      of a mechanism.
-    * range parameters: the value of range parameters is defined for each instance
-      of the mechanism on a cell. For density mechanisms, this means one value for
-      each compartment on which it is present.
+Mechanism catalogues
+''''''''''''''''''''
 
-    The method for setting a parameter depends on its type.
-    If global parameters change, we are effectively defining a new type
-    of mechanism, so global parameter information is encoded in the
-    name.
-    Range parameters are set using a dictionary of name-value pairs.
+.. py:class:: catalogue
 
-    .. code-block:: Python
+    A *mechanism catalogue* is a collection of mechanisms that maintains:
 
-        import arbor
+    1. Collection of mechanism metadata indexed by name.
+    2. A further hierarchy of *derived* mechanisms, that allow specialization of
+       global parameters, ion bindings, and implementations.
 
-        # hh dynamics with default parameters.
-        hh = arbor.mechanism('hh')
+    .. py:method:: has(name)
 
-        # A passive leaky channel with custom parameters
-        pas = arbor.mechanism('pas', {'e': -55, 'gl': 0.02})
+        Test if mechanism with *name* is in the catalogue.
 
-        # Reversal potential using Nernst equation with GLOBAL parameter values
-        # for Faraday's constant and the target ion species, set with a '/' followed
-        # by comma-separated list of parameter after the base mechanism name.
-        rev = arbor.mechanism('nernst/F=96485,x=ca')
+        :param name: name of mechanism.
+        :type name: str
+        :return: bool
 
-    .. method:: mechanism(name, params)
+    .. py:method:: is_derived(name)
 
-        constructor for mechanism with *name* and range parameter overrides *params*,
-        for example: ``arbor.mechanism(name='pas', params={'g': 0.01})``.
+        Is *name* a derived mechanism or can it be implicitly derived?
 
         :param name: name of mechanism.
         :type name: str
-        :param params: A dictionary of parameter values, with parameter name as key.
-        :type params: dict[str, double]
-
-    .. method:: mechanism(name)
-        :noindex:
-
-        constructor for mechanism.
-        The *name* can be either the name of a mechanism in the catalogue,
-        e.g.  ``arbor.mechanism('pas')``, or an implicitly derived mechanism,
-        e.g. ``arbor.mechanism('nernst/k')``.
+        :return: bool
 
-    .. method:: set(name, value)
+    .. py:method:: __getitem__(name)
 
-        Set new value for a parameter.
+        Look up mechanism meta data with *name*.
 
-        :param name: name of the parameter.
-        :type name: str
-        :param value: value of the parameter.
-        :type value: float
+        .. code-block:: Python
 
-    .. py:attribute:: name
-        :type: str
+            import arbor
 
-        The name of the mechanism.
+            cat = arbor.default_catalogue()
 
-    .. py:attribute:: values
-        :type: dict
+            # Print default value and units for gnabar parameter of hh.
+            print(cat['hh'].parameters['gnabar'])
 
-        A dictionary of key-value pairs for the parameters.
+        :param name: name of mechanism.
+        :type name: str
+        :return: mechanism metadata
+        :rtype: :class:`mechanism_info`
 
-    .. code-block:: Python
+    .. py:method:: derive(name, parent, globals={}, ions={})
 
-        import arbor
+        Derive a new mechanism with *name* from the mechanism *parent*.
 
-        # Create pas mechanism with default parameter values (set in NOMDL file).
-        m1 = arbor.mechanism('passive')
+        If no parameters or ion renaming are specified with *globals* or *ions*,
+        the method will attempt to implicitly derive a new mechanism from parent by parsing global and
+        ions from the parent string.
 
-        # Create default mechainsm with custom conductance (range).
-        m2 = arbor.mechanism('passive', {'g', 0.1})
+        .. code-block:: Python
 
-        # Create a new pas mechanism with that changes reversal potential (global).
-        m3 = arbor.mechanism('passive/el=-45')
+            import arbor
 
-        # Create an instance of the same mechanism, that also sets conductance (range).
-        m4 = arbor.mechanism('passive/el=-45', {'g', 0.1})
+            cat = arbor.default_catalogue()
 
-        # This is an equivalent to m4, using set method to specify range parameters.
-        m5 = arbor.mechanism('passive/el=-45')
-        m5.set('g', 0.1)
+            # Use the value of the Faraday constant as published by CODATA in 1986,
+            # and bind to pottasium ion species.
+            cat.derive('krev',  'nernst', globals={'F': 96485.309}, ions={'x': 'k'})
 
-        # Decorate the 'soma' on a cable_cell.
+            # Derive a reversal potential mechanism for sodium from the one we defined
+            # for potasium, which will inherit the redefined Faraday constant.
+            cat.derive('narev', 'krev', ions={'k': 'na'})
 
-        cell.paint('"soma"', m1)
-        cell.paint('"soma"', m2) # Error: can't place the same mechanism on overlapping regions
-        cell.paint('"soma"', m3) # This would be ok: m3 is a new, derived mechanism by virtue of
-                                 # having a different name, i.e. 'passive/el=-45' vs. 'passive'.
+            # Alternatively, we can derive a mechanism with global parameters and ion renaming
+            # specified in the parent name string.
+            cat.derive('krev_imp', 'nernst/F=96485.309,k')
+            cat.derive('carev', 'krev_imp/ca')
 
+        :param name: name of new derived mechanism.
+        :type name: str
+        :param parent: name of parent mechanism.
+        :type parent: str
+        :param globals: a dictionary mapping global parameter names to their values, if any.
+        :type globals: dict[str, float]
+        :param ions: a dictionary renaming ion species, if any.
+        :type ions: dict[str, str]
diff --git a/doc/py_morphology.rst b/doc/py_morphology.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d49a958bec3171ad45fde073d4ee324b1de32608
--- /dev/null
+++ b/doc/py_morphology.rst
@@ -0,0 +1,371 @@
+.. _py_morphology:
+
+Cell morphology
+===============
+
+.. currentmodule:: arbor
+
+.. data:: mnpos
+    :type: int
+
+    Value used to indicate "no parent" in :class:`segment_tree` and :class:`morphology`
+    trees of segments and branches respectively.
+
+    .. code-block:: python
+
+        import arbor
+
+        tree = arbor.segment_tree()
+
+        # mnpos can be used to explicitly specify that a segment
+        # is at the root of the tree. More than one segment can
+        # be at the root, and they will all be joined electrically
+        # at their proximal ends.
+        tree.append(parent=arbor.mnpos, # attach segment to root.
+                    prox=arbor.mpoint(0, 0,-5, 5),
+                    dist=arbor.mpoint(0, 0, 5, 5),
+                    tag=1)
+        tree.append(parent=0,
+                    prox=arbor.mpoint(0, 0, 5, 0.5),
+                    dist=arbor.mpoint(0, 0,50, 0.2),
+                    tag=3)
+
+        # mnpos can also be used when querying a sample_tree or morphology,
+        # for example the following snippet that finds all branches in the
+        # morphology that are attached to the root of the morphology.
+        m = arbor.morphology(tree)
+        base_branches = [i for i in range(m.num_branches)
+                            if m.branch_parent(i) == arbor.mnpos]
+
+        print(base_branches)
+
+
+
+.. class:: location
+
+    A location on :attr:`branch`, where :attr:`pos`, in the range ``0 ≤ pos ≤ 1``,
+    gives the relative position
+    between the proximal and distal ends of the branch. The position is in terms
+    of branch path length, so for example, on a branch of path length 100 μm ``pos=0.2``
+    corresponds to 20 μm and 80 μm from the proximal and distal ends of the
+    branch respectively.
+
+    .. function:: location(branch, pos)
+
+        Constructor.
+
+    .. attribute:: branch
+        :type: int
+
+        The branch id of the location.
+
+    .. attribute:: pos
+        :type: float
+
+        The relative position of the location on the branch.
+
+.. class:: cable
+
+    An unbranched cable that is a subset of a branch.
+    The values of ``0 ≤ prox ≤ dist ≤ 1`` are the relative position
+    of the cable's end points on the branch, in terms
+    of branch path length. For example, on a branch of path length 100 μm, the values
+    :attr:`prox` =0.2, :attr:`dist` =0.8 describe a cable that starts and
+    ends 20 μm and 80 μm along the branch respectively.
+
+    .. function:: cable(branch, prox, dist)
+
+        Constructor.
+
+    .. attribute:: branch
+        :type: int
+
+        The branch id of the cable.
+
+    .. attribute:: prox
+        :type: float
+
+        The relative position of the proximal end of the cable on the branch.
+
+    .. attribute:: dist
+        :type: float
+
+        The relative position of the distal end of the cable on the branch.
+
+.. class:: mpoint
+
+    A location of a cell morphology at a fixed location in space. Describes the location
+    of the as three-dimensional coordinates (:attr:`x`, :attr:`y`, :attr:`z`) and
+    the :attr:`radius` of the cable.
+
+    .. attribute:: x
+        :type: real
+
+        X coordinate (μm)
+
+    .. attribute:: y
+        :type: real
+
+        Y coordinate (μm)
+
+    .. attribute:: z
+        :type: real
+
+        x coordinate (μm)
+
+    .. attribute:: radius
+        :type: real
+
+        Radius of the cable (μm)
+
+.. class:: segment
+
+    .. attribute:: prox
+        :type: mpoint
+
+        The location and radius at the proximal end of the segment.
+
+    .. attribute:: dist
+        :type: mpoint
+
+        The location and radius at the distal end of the segment.
+
+    .. attribute:: tag
+        :type: int
+
+        Integer tag meta-data associated with the segment.
+        Typically the tag would correspond to the SWC structure identifier:
+        soma=1, axon=2, dendrite=3, apical dendrite=4, however arbitrary
+        tags, including zero and negative values, can be used.
+
+.. class:: segment_tree
+
+    A segment tree is a description of a the segments and their connections
+    Segment trees comprise a sequence of segments starting from at lease one *root* segment,
+    together with a parent-child adjacency relationship where a child segment is
+    distal to its parent.
+    Branches in the tree occur where a segment has more than one child.
+    Furthermore, a segment can not have more than one parent.
+    In this manner, neuron morphologies are modeled as a *tree*, where cables that
+    represent dendrites and axons can branch, but branches can not rejoin.
+    A segment tree is a segment-based description of a cell's morphology.
+
+    .. function:: segment_tree()
+
+        Construct an empty segment tree.
+
+    The tree is constructed by *appending* segments to the tree.
+    Segments are numbered starting at 0 in the order that they are added,
+    with the first segment getting id 0, the second segment id 1, and so forth.
+
+    A segment can not be added before its parent, hence the first segment
+    is always at the root. In this manner, a segment tree is
+    always guarenteed to be in a correct state, with consistent parent-child
+    indexing, and with *n* segments numbered from *0* to *n-1*.
+
+    To illustrate how a segment tree is constructed by appending segments,
+    take the segment tree used in the :ref:`documentation above <morph-label-seg-fig>`.
+
+
+    .. figure:: gen-images/label_seg.svg
+
+
+    Which is constructed as follows.
+
+    .. _morph-label-seg-code:
+
+    .. code-block:: Python
+
+        import arbor
+        from arbor import mpoint
+        from arbor import mpos
+
+        tree = arbor.segment_tree()
+        # Start with a cylinder segment for the soma (with tag 1)
+        tree.append(mnpos, mpoint(0,   0.0, 0, 2.0), mpoint( 4,  0.0, 0, 2.0), tag=1)
+        # Construct the first section of the dendritic tree,
+        # comprised of segments 1 and 2, attached to soma segment 0.
+        tree.append(0,     mpoint(4,   0.0, 0, 0.8), mpoint( 8,  0.0, 0, 0.8), tag=3)
+        tree.append(1,     mpoint(8,   0.0, 0, 0.8), mpoint(12, -0.5, 0, 0.8), tag=3)
+        # Construct the rest of the dendritic tree.
+        tree.append(2,     mpoint(12, -0.5, 0, 0.8), mpoint(20,  4.0, 0, 0.4), tag=3)
+        tree.append(3,     mpoint(20,  4.0, 0, 0.4), mpoint(26,  6.0, 0, 0.2), tag=3)
+        tree.append(2,     mpoint(12, -0.5, 0, 0.5), mpoint(19, -3.0, 0, 0.5), tag=3)
+        tree.append(5,     mpoint(19, -3.0, 0, 0.5), mpoint(24, -7.0, 0, 0.2), tag=3)
+        tree.append(5,     mpoint(19, -3.0, 0, 0.5), mpoint(23, -1.0, 0, 0.2), tag=3)
+        tree.append(7,     mpoint(23, -1.0, 0, 0.2), mpoint(26, -2.0, 0, 0.2), tag=3)
+        # Two segments that define the axon, with the first at the root, where its proximal
+        # end will be connected with the proximal end of the soma segment.
+        tree.append(mnpos, mpoint(0,   0.0, 0, 2.0), mpoint(-7,  0.0, 0, 0.4), tag=2)
+        tree.append(9,     mpoint(-7,  0.0, 0, 0.4), mpoint(-10, 0.0, 0, 0.4), tag=2)
+
+    .. method:: append(parent, prox, dist, tag)
+
+        Append a segment to the tree.
+
+        :return: index of the new segment
+        :param int parent: index of segment
+        :param mpoint prox: proximal end of the segment
+        :param mpoint dist: distal end of the segment
+        :param int tag: tag meta data of segment
+
+    .. method:: append(parent, dist, tag)
+        :noindex:
+
+        Append a segment to the tree whose proximal end has the location and
+        radius of the distal end of the parent segment.
+
+        This version of append can't be used for a segment at the root of the
+        tree, that is, when ``parent`` is :data:`mnpos`, in which case both proximal
+        and distal ends of the segment must be specified.
+
+        :return: index of the new segment
+        :param int parent: index of segment
+        :param mpoint dist: distal end of the segment
+        :param int tag: tag meta data of segment
+
+    .. method:: append(parent, x, y, z, radius, tag)
+        :noindex:
+
+        Append a segment to the tree whose proximal end has the location and
+        radius of the distal end of the parent segment.
+
+        This version of append can't be used for a segment at the root of the
+        tree, that is, when ``parent`` is :data:`mnpos`, in which case both proximal
+        and distal ends of the segment must be specified.
+
+        :return: index of the new segment
+        :param int parent: index of segment
+        :param float x: distal x coordinate (μm)
+        :param float y: distal y coordinate (μm)
+        :param float z: distal z coordinate (μm)
+        :param float radius: distal radius (μm)
+        :param int tag: tag meta data of segment
+
+    .. attribute:: empty
+        :type: bool
+
+        If the tree is empty (i.e. whether it has size 0)
+
+    .. attribute:: size
+        :type: int
+
+        The number of segments.
+
+    .. attribute:: parents
+        :type: list
+
+        A list of parent indexes of the segments.
+
+    .. attribute:: segments
+        :type: list
+
+        A list of the segments.
+
+.. py:function:: load_swc(filename)
+
+    Loads the morphology in an SWC file as a :class:`segment_tree`.
+
+    The samples in the SWC files are treated as the end points of segments, where a
+    sample and its parent form a segment.
+    The :py:attr:`tag <segment.tag>` of each segment is the
+    `structure identifier <http://www.neuronland.org/NLMorphologyConverter/MorphologyFormats/SWC/Spec.html>`_
+    of the distal sample.
+    The structure identifier of the first (root) sample is ignored, as it can only be the
+    proximal end of any segment.
+
+    .. note::
+        This method does not interpret the first sample, typically associated with the soma,
+        as a sphere. SWCs with single point somas are, unfortunately, reasonably common, for example
+        `SONATA <https://github.com/AllenInstitute/sonata/blob/master/docs/SONATA_DEVELOPER_GUIDE.md#representing-biophysical-neuron-morphologies>`_
+        model descriptions.
+
+        Such representations are unfortunate because simulation tools like Arbor and NEURON require
+        the use of cylinders or fustrums to describe morphologies, and it is not possible to
+        infer how branches attached to the soma should be connected.
+
+        The :func:`load_swc_allen` function provides support for interpreting
+        such SWC files.
+
+
+    :param str filename: the name of the SWC file.
+    :rtype: segment_tree
+
+.. py:function:: load_swc_allen(filename, no_gaps=False)
+
+    Generate a segment tree from an SWC file following the rules prescribed by
+    AllenDB and Sonata. Specifically:
+
+        * The first sample (the root) is treated as the center of the soma.
+        * The morphology is translated such that the soma is centered at (0,0,0).
+        * The first sample has tag 1 (soma).
+        * All other samples have tags 2, 3 or 4 (axon, apic and dend respectively)
+
+    SONATA prescribes that there should be no gaps, however some models in AllenDB
+    have gaps between the start of sections and the soma. The ``no_gaps`` flag can be
+    used to enforce this requirement.
+
+    Arbor does not support modelling the soma as a sphere, so a cylinder with length
+    equal to the soma diameter is used. The cylinder is centered on the origin, and
+    aligned along the z axis.
+    Axons and apical dendrites are attached to the proximal end of the cylinder, and
+    dendrites to the distal end, with a gap between the start of each branch and the
+    end of the soma cylinder to which it is attached.
+
+    :param str filename: the name of the SWC file.
+    :param bool no_gaps: enforce that distance between soma center and branches attached to soma is the soma radius.
+    :rtype: segment_tree
+
+.. py:class:: morphology
+
+    A *morphology* describes the geometry of a cell as unbranched cables
+    with variable radius and their associated tree structure.
+
+    .. note::
+        A morphology takes a segment tree and construct the cable branches.
+        Meta data about branches and their properties that may be expensive to calculate
+        is stored for fast look up during later stages of model building, and
+        querying by users.
+
+        For this reason, morpholgies are read only. To change a morphology, a new
+        morphology should be created using a new segment tree.
+
+    There is one *constructor* for a morphology:
+
+    .. function:: morphology(segment_tree)
+
+        Construct from a segment tree.
+
+    The morphology provides an interface for querying morphology properties:
+
+    .. attribute:: empty
+            :type: bool
+
+            Indicates if the morphology is empty.
+
+    .. attribute:: num_branches
+            :type: int
+
+            The number of branches in the morphology.
+
+    .. method:: branch_parent(i)
+
+            The parent branch of a branch.
+
+            :param int i: branch index
+            :rtype: int
+
+    .. method:: branch_children(i)
+
+            The child branches of a branch.
+
+            :param int i: branch index
+            :rtype: list
+
+    .. method:: branch_segments(i)
+
+            A list of the segments in a branch, ordered from proximal to distal.
+
+            :param int i: branch index
+            :rtype: list
+
diff --git a/doc/py_intro.rst b/doc/py_overview.rst
similarity index 69%
rename from doc/py_intro.rst
rename to doc/py_overview.rst
index 5b020f8052400cfb216a560dbc5cc66651efc6c6..e85f374f3097492f2a0cf34a8fa6a82e223ef40e 100644
--- a/doc/py_intro.rst
+++ b/doc/py_overview.rst
@@ -5,22 +5,8 @@ Overview
 The Python frontend for Arbor is an interface that the vast majority of users will use to interact with Arbor.
 This section covers how to use the frontend with examples and detailed descriptions of features.
 
-.. _prerequisites:
-
-Prerequisites
-~~~~~~~~~~~~~
-
-Once Arbor has been built and installed (see the :ref:`installation guide <installarbor>`),
-the location of the installed module needs to be set in the ``PYTHONPATH`` environment variable.
-For example:
-
-.. code-block:: bash
-
-    export PYTHONPATH="/usr/lib/python3.7/site-packages/:$PYTHONPATH"
-
-With this setup, Arbor's python module :py:mod:`arbor` can be imported with python3 via
-
-    >>> import arbor
+.. Note::
+    If you haven't set up Arbor yet, see the :ref:`Python installation guide <gs_python>`.
 
 .. _simsteps:
 
@@ -35,7 +21,7 @@ in Python as follows:
 3. Partition the model over the hardware resources using :class:`arbor.partition_load_balance`;
 4. Run the model by initiating then running the :class:`arbor.simulation`.
 
-These details are described and examples are given in the next sections :ref:`pycommon`, :ref:`pyrecipe`, :ref:`pydomdec`, :ref:`pysimulation`, and :ref:`pyprofiler`.
+These details are described and examples are given in the next sections :ref:`pycell`, :ref:`pyrecipe`, :ref:`pydomdec`, :ref:`pysimulation`, and :ref:`pyprofiler`.
 
 .. note::
 
diff --git a/doc/py_profiler.rst b/doc/py_profiler.rst
index 1f26bb339965627893ec0b8594f33c0630d45c72..9df7a77bfe32214416b56df7be037be463eec40e 100644
--- a/doc/py_profiler.rst
+++ b/doc/py_profiler.rst
@@ -2,7 +2,7 @@
 
 .. currentmodule:: arbor
 
-Metering
+Profiler
 ========
 
 Arbor's python module :py:mod:`arbor` has a :class:`meter_manager` for measuring time (and if applicable memory) consumptions of regions of interest in the python code.
@@ -11,7 +11,7 @@ Users manually instrument the regions to measure.
 This allows the user to only measure the parts of the python code that are of interest.
 Once a region of code is marked for the :class:`meter_manager`, the application will track the total time (and memory) spent in this region.
 
-Marking Metering Regions
+Marking metering regions
 ------------------------
 
 First the :class:`meter_manager` needs to be initiated, then the metering started and checkpoints set,
@@ -76,7 +76,7 @@ For instance, the following python code will record and summarize the total time
         meter_manager.checkpoint('simulation-run', context)
 
 
-Metering Output
+Metering output
 ------------------
 
 At any point a summary of the timing regions can be obtained by the :class:`meter_report`.
diff --git a/doc/py_recipe.rst b/doc/py_recipe.rst
index 9f6577ceb8a616640bcc5c8b0593699cfa132b2e..73561f05a715ac27c9eaf397239e2e79db38a674 100644
--- a/doc/py_recipe.rst
+++ b/doc/py_recipe.rst
@@ -10,6 +10,9 @@ The :class:`recipe` class documentation is below.
 A recipe describes neuron models in a cell-oriented manner and supplies methods to provide cell information.
 Details on why Arbor uses recipes and general best practices can be found in :ref:`modelrecipe`.
 
+Recipe
+------
+
 .. class:: recipe
 
     Describe a model by describing the cells and network, without any information about how the model is to be represented or executed.
@@ -34,10 +37,10 @@ Details on why Arbor uses recipes and general best practices can be found in :re
 
     .. function:: cell_description(gid)
 
-        A high level decription of the cell with global identifier :attr:`arbor.cell_member.gid`,
+        A high level description of the cell with global identifier :attr:`arbor.cell_member.gid`,
         for example the morphology, synapses and ion channels required to build a multi-compartment neuron.
         The type used to describe a cell depends on the kind of the cell.
-        The interface for querying the kind and description of a cell are seperate
+        The interface for querying the kind and description of a cell are separate
         to allow the cell type to be provided without building a full cell description,
         which can be very expensive.
 
@@ -100,6 +103,19 @@ Details on why Arbor uses recipes and general best practices can be found in :re
         By default throws a runtime error. If :func:`num_probes`
         returns a non-zero value, this must also be overridden.
 
+Cells
+------
+
+See :ref:`pycell`.
+
+Synapses
+--------
+
+See :ref:`pysynapses`.
+
+Probes
+------
+
 .. class:: probe
 
         Describes the cell probe's information.
@@ -120,71 +136,7 @@ Details on why Arbor uses recipes and general best practices can be found in :re
             loc   = arbor.location(0, 0)    # at the soma
             probe = arbor.cable_probe('voltage', id, loc)
 
-.. class:: connection
-
-    Describes a connection between two cells:
-    Defined by source and destination end points (that is pre-synaptic and post-synaptic respectively),
-    a connection weight and a delay time.
-
-    .. function:: connection(source, destination, weight, delay)
-
-        Construct a connection between the :attr:`source` and the :attr:`dest` with a :attr:`weight` and :attr:`delay`.
-
-    .. attribute:: source
-
-        The source end point of the connection (type: :class:`arbor.cell_member`).
-
-    .. attribute:: dest
-
-        The destination end point of the connection (type: :class:`arbor.cell_member`).
-
-    .. attribute:: weight
-
-        The weight delivered to the target synapse.
-        The weight is dimensionless, and its interpretation is specific to the type of the synapse target.
-        For example, the expsyn synapse interprets it as a conductance with units μS (micro-Siemens).
-
-    .. attribute:: delay
-
-        The delay time of the connection [ms]. Must be positive.
-
-    An example of a connection reads as follows:
-
-    .. container:: example-code
-
-        .. code-block:: python
-
-            import arbor
-
-            # construct a connection between cells (0,0) and (1,0) with weight 0.01 and delay of 10 ms.
-            src  = arbor.cell_member(0,0)
-            dest = arbor.cell_member(1,0)
-            w    = 0.01
-            d    = 10
-            con  = arbor.connection(src, dest, w, d)
-
-.. class:: gap_junction_connection
-
-    Describes a gap junction between two gap junction sites.
-    Gap junction sites are represented by :class:`arbor.cell_member`.
-
-    .. function::gap_junction_connection(local, peer, ggap)
-
-        Construct a gap junction connection between :attr:`local` and :attr:`peer` with conductance :attr:`ggap`.
-
-    .. attribute:: local
-
-        The gap junction site: one half of the gap junction connection.
-
-    .. attribute:: peer
-
-        The gap junction site: other half of the gap junction connection.
-
-    .. attribute:: ggap
-
-        The gap junction conductance [μS].
-
-Event Generator and Schedules
+Event generator and schedules
 -----------------------------
 
 .. class:: event_generator
@@ -295,74 +247,8 @@ An example of an event generator reads as follows:
         w      = 0.1
         gen    = arbor.event_generator(target, w, sched)
 
-Cells
-------
-
-.. class:: lif_cell
-
-    A benchmarking cell (leaky integrate-and-fire), used by Arbor developers to test communication performance,
-    with neuronal parameters:
-
-    .. attribute:: tau_m
-
-        Membrane potential decaying constant [ms].
-
-    .. attribute:: V_th
-
-        Firing threshold [mV].
-
-    .. attribute:: C_m
-
-        Membrane capacitance [pF].
-
-    .. attribute:: E_L
-
-        Resting potential [mV].
-
-    .. attribute:: V_m
-
-        Initial value of the Membrane potential [mV].
-
-    .. attribute:: t_ref
-
-        Refractory period [ms].
-
-    .. attribute:: V_reset
-
-        Reset potential [mV].
-
-.. class:: spike_source_cell
-
-    A spike source cell, that generates a user-defined sequence of spikes
-    that act as inputs for other cells in the network.
-
-    .. function:: spike_source_cell(schedule)
-
-        Construct a spike source cell that generates spikes
-
-        - at regular intervals (using an :class:`arbor.regular_schedule`)
-        - at a sequence of user-defined times (using an :class:`arbor.explicit_schedule`)
-        - at times defined by a Poisson sequence (using an :class:`arbor.poisson_schedule`)
-
-        :param schedule: User-defined sequence of time points (choose from :class:`arbor.regular_schedule`, :class:`arbor.explicit_schedule`, or :class:`arbor.poisson_schedule`).
-
-.. class:: benchmark_cell
-
-    A benchmarking cell, used by Arbor developers to test communication performance.
-
-    .. function:: benchmark_cell(schedule, realtime_ratio)
-
-        A benchmark cell generates spikes at a user-defined sequence of time points:
-
-        - at regular intervals (using an :class:`arbor.regular_schedule`)
-        - at a sequence of user-defined times (using an :class:`arbor.explicit_schedule`)
-        - at times defined by a Poisson sequence (using an :class:`arbor.poisson_schedule`)
-
-        and the time taken to integrate a cell can be tuned by setting the parameter ``realtime_ratio``.
-
-        :param schedule: User-defined sequence of time points (choose from :class:`arbor.regular_schedule`, :class:`arbor.explicit_schedule`, or :class:`arbor.poisson_schedule`).
-
-        :param realtime_ratio: Time taken to integrate a cell, for example if ``realtime_ratio`` = 2, a cell will take 2 seconds of CPU time to simulate 1 second.
+Example
+-------
 
 Below is an example of a recipe construction of a ring network of multi-compartmental cells.
 Because the interface for specifying cable morphology cells is under construction, the temporary
diff --git a/doc/py_reference.rst b/doc/py_reference.rst
new file mode 100644
index 0000000000000000000000000000000000000000..bf0b84e91c2f4414d24a6686dd9dee44d4e90ab6
--- /dev/null
+++ b/doc/py_reference.rst
@@ -0,0 +1,10 @@
+Python API Reference
+====================
+
+This page contains the full Python API reference. You can safely leave out `._arbor` in your own code; it's autoimported into the `arbor` package. In an interactive Python interpreter, you can use ``help()`` on any class or function to obtain the documentation you read below. E.g. ``help(arbor.gap_junction_connection)`` will print :class:`this<arbor._arbor.gap_junction_connection>`.
+
+.. automodule:: arbor._arbor
+    :members:
+    :undoc-members:
+    :special-members:
+    :inherited-members:
diff --git a/doc/requirements.txt b/doc/requirements.txt
index c4e363475fc7c5aa3fadbe0113a1f914ccc12ce0..9135cbf4b19ea13099e3b1162804ed6dfdf140d5 100644
--- a/doc/requirements.txt
+++ b/doc/requirements.txt
@@ -1,2 +1,2 @@
 sphinx>=2.3.0
-svgwrite
+svgwrite
\ No newline at end of file
diff --git a/doc/scripts/make_images.py b/doc/scripts/make_images.py
index 0df83945c9063a7b80ce3fd821eae19b240c6b1b..956971a9b35daf1296108339fa519c47b013373d 100644
--- a/doc/scripts/make_images.py
+++ b/doc/scripts/make_images.py
@@ -2,6 +2,8 @@ import copy
 import svgwrite
 import math
 import inputs
+import seaborn
+import pandas
 
 tag_colors = ['white', '#ffc2c2', 'gray', '#c2caff']
 
@@ -9,6 +11,16 @@ tag_colors = ['white', '#ffc2c2', 'gray', '#c2caff']
 # ############################################
 #
 
+def dataframe_line_plot(input_csv, output_svg):
+    print('generating:', output_svg)
+    dataframe = pandas.read_csv(input_csv,index_col=0)
+    axes = dict(zip(['x','y','hue','col','style'],dataframe.columns.values)) # 5D seems enough for now.
+    seaborn.relplot(data=dataframe, kind="line", **axes).savefig(output_svg)
+
+#
+# ############################################
+#
+
 def translate(x, f, xshift):
     return (f*x[0]+xshift, -f*x[1])
 
@@ -280,6 +292,8 @@ def generate(path=''):
     label_image(inputs.label_morph, [inputs.reg_radgt5],  path+'/radiusgt_label.svg')
     label_image(inputs.label_morph, [inputs.reg_radge5],  path+'/radiusge_label.svg')
 
+    # dataframe_line_plot(path+'/../images/single_cell_model_result.csv', path+'/single_cell_model_result.svg')
+
 
 if __name__ == '__main__':
     generate('.')
diff --git a/doc/single_cell.rst b/doc/single_cell.rst
deleted file mode 100644
index f60f37f0686e7cc057f0552abc458823d4e5c61e..0000000000000000000000000000000000000000
--- a/doc/single_cell.rst
+++ /dev/null
@@ -1,141 +0,0 @@
-.. _single:
-
-Single Cell Models
-==================
-
-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 modeling concepts and approach.
-
-This guide will walk through a series of single cell models of increasing
-complexity that the reader is encouraged to follow along with in Python. Links
-are provide to separate documentation that covers relevant topics in more detail.
-
-.. _single_soma:
-
-Example 1: Single compartment cell with HH dynamics
-----------------------------------------------------
-
-The most trivial representation of a cell in Arbor is to model the entire cell as a sphere.
-The following model shows the steps required to construct a model of a spherical cell with
-radius 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, spike detectors and electrical properties.
-
-Our "single-compartment HH cell" has a trivial morphology and dynamics, so the steps to
-create the ``cable_cell`` that represents it are quite straightforward:
-
-.. code-block:: python
-
-    import arbor
-
-    # (1) Create a morphology with a single segment of length=diameter=6 μm
-    tree = arbor.segment_tree()
-    tree.append(arbor.mnpos, arbor.mpoint(-3, 0, 0, 3), arbor.mpoint(3, 0, 0, 3), tag=1)
-
-    # (2) Define the soma and its center
-    labels = arbor.label_dict({'soma':   '(tag 1)',
-                               'center': '(location 0 0.5)'})
-
-    # (3) Create cell and set properties
-    cell = arbor.cable_cell(tree, labels)
-    cell.set_properties(Vm=-40)
-    cell.paint('soma', 'hh')
-    cell.place('center', arbor.iclamp( 10, 2, 0.8))
-    cell.place('center', arbor.spike_detector(-10))
-
-Arbor's cell morphologies are constructed from a :ref:`segment tree<morph-segment_tree>`,
-which is a list of segments, which are tapered cones with a *tag*.
-Step **(1)** above shows how the spherical cell is represented using a single segment.
-
-Cell builders need to refer to *regions* and *locations* on a cell morphology.
-Arbor uses a domains specific language (DSL) to describe regions and locations,
-which are given labels. In step **(2)** a dictionary of labels is created
-with two labels:
-
-* ``soma`` defines a *region* with ``(tag  2)``. Note that this corresponds to the ``tag`` parameter that was used to define the single segment in step (1).
-* ``center`` defines a *location* at ``(location 0 0.5)``, which is the mid point ``0.5`` of branch ``0``, which corresponds to the center of the soma on the morphology defined in Step (1).
-
-In step **(3)** the cable cell is constructed by combining the segment tree with
-the named regions and locations.
-
-* Set initial membrane potential everywhere on the cell to -40 mV.
-* Use HH dynamics on soma.
-* Attach stimuli with duration of 2 ms and current of 0.8 nA.
-* Add a spike detector with threshold of -10 mV.
-
-Arbor can simulate networks with multiple individual cells, connected together in a network.
-Single cell models do not require the full *recipe* interface used to describing such
-network models, with many unique cells, network and gap junctions.
-Arbor provides a ``single_cell_model`` helper that wraps a cell description, and provides
-an interface for recording 
-
-
-.. code-block:: python
-
-    # (4) Make single cell model.
-    m = arbor.single_cell_model(cell)
-
-    # (5) Attach voltage probe sampling at 10 kHz (every 0.1 ms).
-    m.probe('voltage', 'center', frequency=10000)
-
-    # (6) Run simulation for 100 ms of simulated activity.
-    m.run(tfinal=100)
-
-Step (4) instantiates the single cell model using our single-compartment cell.
-To record variables from the model three pieces of information are provided.
-
-.. _single_morpho:
-
-Morphology
-----------
-
-.. warning::
-
-    The flat cell builder documented below is likely to change before
-    version 0.4 is released. It is a wrapper around a segment tree that
-    simplifies morphology construction when the spatial location of segments
-    is not important (for example, when we only need to describe the length,
-    radius and parent-child relationship of branches in the morphology).
-
-The first step in building a cell model is to define the cell's *morphology*.
-Conceptually, Arbor treats morphologies as a tree of truncated frustums.
-These are represented as a tree of segments, where each segment is defined
-by two end points with radius, and a tag, and a parent segment to which it is attached.
-
-Let's start with a simple "ball and stick" model cell, with a spherical soma
-(the ball, treated as a cylinder with length=with) and a y-shaped dendritic
-tree (the sticks).
-
-.. container:: example-code
-
-    .. code-block:: python
-
-        import arbor
-        builder = arbor.flat_cell_builder()
-
-        # Start with the soma, which is a cylinder with the same area as
-        # a sphere of radius 5 μm. Label this segment 'soma'.
-        p = builder.add_cable(parent=arbor.mnpos, length=10, radius=5, name='soma')
-
-        # Attach a cable to the soma with length 100 μm and constant radius 4 μm.
-        q = builder.add_cable(parent=p, length=100, radius=4, name='dend')
-
-        # Attach two dendrites to the first of length 50 μm, that taper from 4 μm to 2 μm.
-        p = builder.add_cable(parent=q, length=50, radius=(4,2), name='dend')
-        p = builder.add_cable(parent=q, length=50, radius=(4,2), name='dend')
-
-        # Generate the cable cell once all cables have been described.
-        cell = builder.build()
-
-
-There are two approaches available for building a morphology: construct it manually using
-:ref:`segment tree<morph-segment_tree>` or ``flat_cell_builder``, or load from a file.
-The ``flat_cell_builder`` is a helper tool, that internally constructs a segment
-tree.
-
diff --git a/python/pybind11 b/python/pybind11
index 3b1dbebabc801c9cf6f0953a4c20b904d444f879..e2b884c33bcde70b2ea562ffa52dd7ebee276d50 160000
--- a/python/pybind11
+++ b/python/pybind11
@@ -1 +1 @@
-Subproject commit 3b1dbebabc801c9cf6f0953a4c20b904d444f879
+Subproject commit e2b884c33bcde70b2ea562ffa52dd7ebee276d50