Compartments vs Segments vs Sections

Managing anatomically complex model cells with the CellBuilder. Importing morphometric data with NEURON's Import3D tool or Robert Cannon's CVAPP. Where to find detailed morphometric data.
Post Reply
jdlawson
Posts: 3
Joined: Thu Apr 20, 2023 3:13 pm

Compartments vs Segments vs Sections

Post by jdlawson »

I am attempting to load a model with NEURON and pull out a list of compartments in the simulation along with connectivity information and the resistance between each pair of connected compartments.

My understanding is that compartments in NEURON are represented by segments, which are organized into groups called sections. Let 'dend[1]' be an example section. dend[1] has dend[1].nseg compartments, but dend[1].nseg + 2 segment objects in the iterator returned by dend[1].allseg(). This is because the endpoint of each section is also modeled using a segment object. My current understanding is that the section endpoints are not actually considered 'compartments' in NEURON's spatial discretization of the cable equation, but the segments in the interior of the section represent the midpoints of each compartment. Is this correct?

Each segment object defines the method ri() which "returns the resistance (in megohms) between the center of the segment and its parent segment." My second question is: how do we know what the parent of a given segment is? Say dend[1].nseg = 3, so list(dend[1].allseg()) is [dend[1](0), dend[1](0.166667), dend[1](0.5), dend[1](0.833333), dend[1](1)]. It seems reasonable to me that the parent segment of dend[1](0) would be dend[1].parentseg(), and the documentation seems to confirm this. I would expect that the 'parent segment' of the other segments in dend[1] would be the preceding segment in the section, so the parent of dend[1](0.166667) would be dend[1](0) and the parent of dend[1](0.5) would be dend[1](0.166667). Is this true?

If the previous paragraph is correct, I would expect dend[1](0.166667).ri() to return the resistance between dend[1](0) and dend[1](0.166667), and dend[1](0).ri() to return the resistance between dend[1].parentseg() and dend[1](0). Lets say that dend[1].parentseg() is the last segment in a previous section, dend[0], which has nseg = 1. Then we could represent the underlying compartmental topology as

Code: Select all

... -- dend[0](0.5) -- r_1 -- dend[1](0.166667) -- r_2 -- dend[1](0.5) -- r_3 -- dend[1](0.833333) -- ...
Where r_1, r_2, and r_3 are resistances between compartments. If I understand correctly, the three resistances could be computed as:

r_1: dend[0](1).ri() + dend[1](0).ri() + dend[1](0.166667).ri()
r_2: dend[1](0.5).ri()
r_3: dend[1](0.833333).ri()

Furthermore, if dend[0] were also connected to another section dend[2] with dend[2].nseg = 3 and dend[2].parentseg() = dend[0](1.), the topology and resistances would be:

Code: Select all

                   /-- r_1 -- dend[1](0.166667) -- r_2 -- dend[1](0.5) -- r_3 -- dend[1](0.833333) -- ...
                  /
... -- dend[0](0.5)
                  \
                   \-- r_4 -- dend[2](0.166667) -- r_5 -- dend[2](0.5) -- r_6 -- dend[2](0.833333) -- ...
With

r_4 = dend[0](1).ri() + dend[2](0).ri() + dend[2](0.166667).ri()
r_5 = dend[2](0.5).ri()
r_6 = dend[2](0.833333).ri()

Is this correct?
ted
Site Admin
Posts: 6289
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Compartments vs Segments vs Sections

Post by ted »

My understanding is that compartments in NEURON are represented by segments, which are organized into groups called sections.
Here's a better way to think about this stuff.

First, it's not about compartments. Nobody ever looks at a biological neuron, or even a stylized ball and stick model, and sees compartments. Well, OK, maybe somebody steeped in CellML does. But the normal reaction is to marvel at the branched architecture of the cell. And this is why NEURON has sections. The formal definition of a section is "an unbranched neurite." Each branch of a neuron can be represented by a section. Sometimes it makes sense to represent a neurite as a chain of two or more sections, e.g. the proximal end of an axon which might be represented with three sections: one for the axon hillock, one for the initial segment, and one for the part of the axon between the distal end of the initial segment and the first point of axonal branching.

Second, having seen all those branches, there is a natural tendency to start asking questions about interesting variables which, at the anatomical scale of real cells, vary continously over time and space. This variation is described by partial differential equations (PDEs), but those are not computationally tractable. Instead, spatial discretization is used to generate a family of ordinary differential equations (ODEs) whose solutions would approximate the original, physically continuous system. Each ODE* governs the time course of a particular variable at a particular location in space called a node. NEURON's spatial discretization strategy is equvalent to representing a section as one or more segments of equal length. Each node is located at the center of a segment, and membrane potential at each of these internal nodes is calculated by numerical integration of an ODE.

In addition to these internal nodes, every section also has a node at each end, but no membrane is associated with these terminal nodes. Terminal nodes are points of attachment to other sections, and the intracellular potential at a terminal node is calculated algebraically from the potentials at adjacent internal nodes by applying Kirchhoff's current law.

*--There's still one more approximation that needs to be made: temporal discretization is applied to the family of ODEs to produce a set of algebraic equations that can be manipulated to produce an approximate solution in time to the approximate solution in space for how the variables of interest vary over time and space.
ted
Site Admin
Posts: 6289
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Compartments vs Segments vs Sections

Post by ted »

Miscellanea:
the iterator returned by dend[1].allseg()
is useful if you want to report the value of v along a path and you want to include the values at the terminal nodes 0 and 1 ends) of sections. It's not useful for transmembrane currents, because those don't exist at terminal nodes (because membrane is associated only with internal nodes). Nor is it useful for specifying or reporting the values of other state variables such as ionic concentrations or equilibrium potentials, or parameters such as ion channel conductance densities. For those purposes the idiom to use is
for seg in sec: . . . do something with the variable of interest . . .
ted
Site Admin
Posts: 6289
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Compartments vs Segments vs Sections

Post by ted »

More miscellanea:
how do we know what the parent of a given segment is?
Let's see if we can think of a way to figure this out.

Try this: make a toy model cell that has three sections called soma, dend, and axon
Create the soma first, then the dend and axon.
Attach the dend's 0 end to the soma's 1 end.
Attach the axon's 0 end to the soma's 0 end.
Set their diam and L to the following values:
soma L = diam = 10
dend L = 100, diam = 2
axon L = 100, diam = 1

Leave nseg == 1.

For each node in soma print ri() at that node.
Do the same for dend and axon.

Based on those results, what rule do you infer? Feel free to describe how you reached that conclusion, but keep the rule itself short and simple.
jdlawson
Posts: 3
Joined: Thu Apr 20, 2023 3:13 pm

Re: Compartments vs Segments vs Sections

Post by jdlawson »

Thanks for the replies!

As per your suggestion, when I run the following code:

Code: Select all

from neuron import h

soma = h.Section(name='soma')
soma.L = 10
soma.diam = 10
soma.nseg = 1

dend = h.Section(name='dend')
dend.L = 100
dend.diam = 2
dend.nseg = 1
dend.connect(soma(1.))

axon = h.Section(name='axon')
axon.L = 100
axon.diam = 1
axon.nseg = 1
axon.connect(soma(0.))

h.finitialize()

for sec in h.allsec():
  for node in sec.allseg():
    print(node, "ri:", node.ri())
It prints

Code: Select all

soma(0) ri: 1e+30
soma(0.5) ri: 0.02253633994181238
soma(1) ri: 0.02253633994181238
dend(0) ri: 0.02253633994181238
dend(0.5) ri: 5.634084985453095
dend(1) ri: 5.634084985453095
axon(0) ri: 1e+30
axon(0.5) ri: 22.53633994181238
axon(1) ri: 22.53633994181238
It seems clear that the middle and distal nodes (e.g. dend(0.5) and dend(1)) are returning the resistance to the nearest proximal segment, i.e. dend(0) and dend(0.5), respectively. This makes sense.

axon(0).ri() and dend(0).ri(), however, are equal to their respective parent segment's ri values. This seems to go against what I said in my earlier post. If my earlier post was correct, the resistance between dend(0) and soma(1) would not equal the resistance between soma(1) and soma(0.5) in this morphology.

I'm not entirely sure what to make of this. I guess if I had to take a guess I'd say that dend(0).ri() is returning the resistance from dend(0) to soma(0.5), which is equal to the resistance from soma(1) to soma(0.5). I think this is the case because soma(0.5) is the nearest proximal internal node to dend(0). I don't feel confident about my guess because this seems to go against the idea of the parent being the nearest proximal segment (internal or not). In my understanding the nearest proximal segment of dend(0) is soma(1). Perhaps there is some special casing at the endpoint nodes?
ted
Site Admin
Posts: 6289
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Compartments vs Segments vs Sections

Post by ted »

Here are the rules.

NEURON accommodates any branched topology (tree) that does not involve a closed path.

The root branch (section) of the tree is the branch that has no parent. All other sections have parents.

The root node of the tree is the 0 node of the root section (the node at the root section's 0 end). This node has no parent node. That's why soma(0).ri() in the toy model is infinite (1e30 is close enough for government work).

The 0 node of a child section refers to the same location as the node of the parent to which it is attached. That's why axon(0).ri() in the toy model is infinite. And it's why dend(0).ri() == soma(1).ri().

For every node that is not a 0 node, the parent node is the adjacent node that is farther from the section's 1 end.
jdlawson
Posts: 3
Joined: Thu Apr 20, 2023 3:13 pm

Re: Compartments vs Segments vs Sections

Post by jdlawson »

Ah ok that makes sense! So circling back to my original question I want to make sure I've got it correct.

For the following topology

Code: Select all

                   /-- r_1 -- dend[1](0.166667) -- r_2 -- dend[1](0.5) -- r_3 -- dend[1](0.833333) -- ...
                  /
... -- dend[0](0.5)
                  \
                   \-- r_4 -- dend[2](0.166667) -- r_5 -- dend[2](0.5) -- r_6 -- dend[2](0.833333) -- ...
the resistances would be

Code: Select all

r_1 = dend[1](0).ri() + dend[1](0.166667).ri() = dend[0](1.).ri() + dend[1](0.166667).ri()
r_2 = dend[1](0.5).ri()
r_3 = dend[1](0.833333).ri()
r_4 = dend[2](0).ri() + dend[1](0.166667).ri() = dend[0](1.).ri() + dend[2](0.166667).ri()
r_5 = dend[2](0.5).ri()
r_6 = dend[2](0.833333).ri()
Is that right?
ted
Site Admin
Posts: 6289
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Compartments vs Segments vs Sections

Post by ted »

For the following topology
That diagram is not consistent with this rule
The 0 node of a child section refers to the same location as the node of the parent to which it is attached.
or the fact that NEURON uses the central difference approximation for the second derivative of membrane potential with respect to space. Time to read something--chapter 4 in the NEURON Book, or if you don't have the book maybe this old preprint will help
https://neuron.yale.edu/ftp/neuron/papers/nsimenv.pdf
especially Figs. 3.3 and 3.4.

I'd point you to something more recent, e.g. Hines et al. "NEURON Simulation Environment" https://doi.org/10.1007/978-1-0716-1006-0_795 in the Encyclopedia of Computational Neuroscience, but most recent sources that actually show helpful diagrams are behind paywalls.
ted
Site Admin
Posts: 6289
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Compartments vs Segments vs Sections

Post by ted »

Perhaps my previous reply was too cryptic.

The problem with the equivalent circuit in your Mon Apr 24, 2023 7:48 pm post is that ignores the fact that the paths between the internal node of dend 0 (i.e. 0.5) and the proximal internal nodes of dends 1 and 2 overlap. Where does this overlap occur? In the axial resistance between the internal and distal nodes of dend 0.

Your formulas for calculating r_1 etc. do produce correct numerical results, but the topology of your equivalent circuit is the topology that would result from attaching the 0 ends of dends 1 and 2 to the 0.5 node of dend 0.
Post Reply