Neuron transforms soma that is loaded from .swc. What is the rule for it?

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
vogdb
Posts: 37
Joined: Sun Aug 13, 2017 9:51 am

Neuron transforms soma that is loaded from .swc. What is the rule for it?

Post by vogdb » Mon Feb 10, 2020 6:58 am

Hi! Can you please point to me to the source code/rule that adds new points to a soma that is loaded from .swc?
An example. Lets say I have a morphology with a soma.

Code: Select all

# index     type         X            Y            Z       radius       parent
1           1    0.000000     0.000000     0.000000     5.442800          -1
2           2     0.292800    -4.265800    -5.562200     0.325200           1
3           2     0.654500    -5.303200    -5.692400     0.359400           2
...
When it's loaded into NEURON, it becomes a cylinder:

Code: Select all

    h_section = soma
    for ipt in range(h_section.n3d()):
        x = h_section.x3d(ipt)
        y = h_section.y3d(ipt)
        z = h_section.z3d(ipt)
        points.append([x, y, z])

    # points
    # [[-5.442800045013428, 0.0, 0.0], [0.0, 0.0, 0.0], [5.442800045013428, 0.0, 0.0]]
But now there is more. If I save this morphology with the new soma and load it again to NEURON, it will be transformed again.
So, the updated morphology now starts as:

Code: Select all

# index     type         X            Y            Z       radius       parent
1           1    -5.442800     0.000000     0.000000     5.442800          -1
2           1     0.000000     0.000000     0.000000     5.442800           1
3           1     5.442800     0.000000     0.000000     5.442800           2
4           2     0.292800    -4.265800    -5.562200     0.325200           1
5           2     0.654500    -5.303200    -5.692400     0.359400           4
...
But points become:

Code: Select all

[[-10.885600090026855, 0.0, 0.0], [-5.442800045013428, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [5.442800045013428, 0.0, 0.0]]
It is like the left part gets its diameter doubled. And there is more. Now there 2 soma sections: Cell_472363762[0].soma[0] and Cell_472363762[0].soma[1]. The latter is the child of the former. It is attached to Cell_472363762[0].soma[0](0.5).
Cell_472363762[0].soma[0] is composed of first 3 points: [[-10.885600090026855, 0.0, 0.0], [-5.442800045013428, 0.0, 0.0], [0.0, 0.0, 0.0]]
Cell_472363762[0].soma[1] is composed of last 2 points: [0.0, 0.0, 0.0], [5.442800045013428, 0.0, 0.0]

Can you please help me to understand the logic of this transformation? I can understand why single point goes to cylinder but the second transformation from 3 points to 5 points is difficult for me.

I use this method to load a morphology:

Code: Select all

proc load_morphology(/* morphology_dir, morphology_name */) {localobj morph, import, sf, extension
  strdef morph_path
  sprint(morph_path, "%s/%s", $s1, $s2)

  sf = new StringFunctions()
  extension = new String()

  sscanf(morph_path, "%s", extension.s)
  sf.right(extension.s, sf.len(extension.s)-4)
  morph = new Import3d_SWC_read()
  morph.quiet = 1
  morph.input(morph_path)

  import = new Import3d_GUI(morph, 0)
  import.instantiate(this)
}

ted
Site Admin
Posts: 5752
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Neuron transforms soma that is loaded from .swc. What is the rule for it?

Post by ted » Mon Feb 10, 2020 12:57 pm

The first line of data in your SWC file

Code: Select all

# index     type         X            Y            Z       radius       parent
1           1    0.000000     0.000000     0.000000     5.442800          -1
is what we call, for lack of a better term, a "one point specification of a soma." There has never been an "official" definition of the SWC format, so it's hard to be dogmatic about this, but "one point somas" did not start to appear until some time (years?) after SWC was already in wide use. The second field contains a 1, which means this line pertains to the soma. The 7th field contains -1, so this particular line specifies the xyz coordinates (0,0,0) and radius (5.4... um) of the root of the branched tree. There are no other soma data lines, so this is a "one point specification of the soma." In other words, the soma is a sphere with center at 0,0,0, and radius 5.4...
When it's loaded into NEURON, it becomes a cylinder
A sphere with radius r has the same surface area as a cylinder with length = diameter = 2*r. NEURON's Import3d code translates an SWC one point soma with radius r into a section (cylinder) with length = diameter = 2*r.

By the way, the statement
h_section = soma
accomplishes no magic. It just creates a Python alias for the python object called soma. Instead of

Code: Select all

h_section = soma
for ipt in range(h_section.n3d()):
  ...etc...
you could have saved some typing by

Code: Select all

for i in range(soma.n3d()):
  x = soma.x3d(i)
  y = soma.y3d(i)
  z = soma.z3d(i)
  d3d = soma.diam3d(i)
  points.append([x, y, z, d3d])
If I save this morphology with the new soma and load it again to NEURON . . .
The expected workflow is that one imports one SWC file and exports either a single cell instance directly to NEURON, or to a CellBuilder that can then be saved to a session file for future reuse.

Exporting directly to NEURON can create either
(1) a model cell whose sections are all "at the top level of the interpreter" (NOT contained in an instance of an object)
or
(2) a model cell whose sections are contained in an object.
The problem with (1) is that it invites abuse, as you have discovered. The code that translates SWC to a NEURON model specification is very good at doing a simple task: converting a single SWC file to a single model specification. But that doesn't mean it can handle all possible tasks that users might imagine. At the very least, creating top level sections by reading multiple SWC files, or re-reading the same file in the same session (i.e. without restarting NEURON after each conversion), is guaranteed to produce naming conflicts (that's why you got so many different sections with the root name "soma").

If you tell me what you are trying to accomplish, perhaps I can suggest a way to do it that won't run into such problems. Are you just trying to use Python to import data from a single SWC file into a single model cell? Or do you have several SWC files that you want to convert into model cells (one SWC file per model cell), and you don't want the conversions to interfere with each other?

vogdb
Posts: 37
Joined: Sun Aug 13, 2017 9:51 am

Re: Neuron transforms soma that is loaded from .swc. What is the rule for it?

Post by vogdb » Tue Feb 11, 2020 5:20 am

Thank you, Ted!
My main question is how I can avoid of adding 2 additional points in case soma is a cylinder already. In order to avoid any misunderstandings here is the code:
SimpleCellCylinder.swc

Code: Select all

# index     type         X            Y            Z       radius       parent
1           1    -5.442800     0.000000     0.000000     5.442800          -1
2           1     0.000000     0.000000     0.000000     5.442800           1
3           1     5.442800     0.000000     0.000000     5.442800           2
4           2     0.292800    -4.265800    -5.562200     0.325200           1
5           2     0.654500    -5.303200    -5.692400     0.359400           4
the corresponding .hoc SimpleCell.hoc

Code: Select all

{load_file("stdrun.hoc")}
{load_file("import3d.hoc")}

begintemplate SimpleCell
  public init
  public soma, dend, apic, axon, myelin
  create soma[1], dend[1], apic[1], axon[1], myelin[1]
  objref this

  public all, somatic, apical, axonal, basal, myelinated
  objref all, somatic, apical, axonal, basal, myelinated

proc init(/* args: morphology_dir, morphology_name */) {
  all = new SectionList()
  apical = new SectionList()
  axonal = new SectionList()
  basal = new SectionList()
  somatic = new SectionList()
  myelinated = new SectionList()

  forall delete_section()

  load_morphology($s1, $s2)
}


proc load_morphology(/* morphology_dir, morphology_name */) {localobj morph, import, sf, extension
  strdef morph_path
  sprint(morph_path, "%s/%s", $s1, $s2)

  sf = new StringFunctions()
  extension = new String()

  sscanf(morph_path, "%s", extension.s)
  sf.right(extension.s, sf.len(extension.s)-4)


  if( strcmp(extension.s, ".swc" ) == 0) {
    morph = new Import3d_SWC_read()
  } else {
    printf(extension.s)
    printf("Unsupported file format: Morphology file has to end with .swc" )
    quit()
  }

  morph.quiet = 1
  morph.input(morph_path)

  import = new Import3d_GUI(morph, 0)
  import.instantiate(this)
}
endtemplate SimpleCell
Python script that tights them together

Code: Select all

def get_section_points(h_section):
    points = []
    for ipt in range(h_section.n3d()):
        x = h_section.x3d(ipt)
        y = h_section.y3d(ipt)
        z = h_section.z3d(ipt)
        d = h_section.diam3d(ipt)
        points.append([x, y, z, d])
    return points


comps_dir = Path('.')
bio_file = comps_dir / 'SimpleCell.hoc'
morph_file = comps_dir / 'SimpleCellCylinder.swc'
h.load_file(str(bio_file))
cell = h.SimpleCell(str(morph_file.parent), morph_file.name)
for soma_section in cell.somatic:
    print(soma_section, get_section_points(soma_section))
The output I receive

Code: Select all

SimpleCell[0].soma[0] [[-10.885600090026855, 0.0, 0.0, 10.885600090026855], [-5.442800045013428, 0.0, 0.0, 10.885600090026855], [0.0, 0.0, 0.0, 10.885600090026855]]
SimpleCell[0].soma[1] [[0.0, 0.0, 0.0, 10.885600090026855], [5.442800045013428, 0.0, 0.0, 10.885600090026855]]
My question. How I can make this output to be? (as it's supposed to be)

Code: Select all

SimpleCell[0].soma[0] [[-5.442800045013428, 0.0, 0.0, 10.885600090026855], [-0.0, 0.0, 0.0, 10.885600090026855], [5.442800045013428, 0.0, 0.0, 10.885600090026855]]
I understand why single point goes to soma but why an initially cylinder soma gets transformed? Why this point [-10.885600090026855, 0.0, 0.0, 10.885600090026855] gets added? It wasn't in 'SimpleCellCylinder.swc'. If you can point to the logic behind it in NEURON sources https://github.com/neuronsimulator/nrn that also would be great.

ted
Site Admin
Posts: 5752
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Neuron transforms soma that is loaded from .swc. What is the rule for it?

Post by ted » Thu Feb 13, 2020 2:07 pm

My question. How I can make this output to be? (as it's supposed to be)
Here's the answer: throw away the Python and hoc code that you are using. The SWC data describes a shape that consists of a soma and an axon. The hoc template is very strange, not remotely relevant to the SWC data, and unnecessary. The Python file is also unnecessary. Instead, use this Python code:

Code: Select all

from neuron import h,gui
h.load_file('import3d.hoc')

class Cell():
  def __init__(self, fname, gid):
    self._gid = gid
    self.fname = fname
    self.load_morphology(fname)
  def load_morphology(self, nom):
    print(nom)
    cell = h.Import3d_SWC_read()
    cell.input(nom)
    i3d = h.Import3d_GUI(cell, 0)
    i3d.instantiate(self)
  def __str__(self):
    return 'Cell[{}]'.format(self._gid)
Usage examples:

Code: Select all

cl10 = Cell('l10.swc', 0) # first argument is name of swc file, second is a gid'
# and if you want to use some other morphology to create another cell instance
cn120 = Cell('n120.swc', 1) # specify a different gid for each cell instance in a model

cl10.soma # returns a list of cl10's N sections named soma[0]..soma[N-1]

# yet to be done by you, as necessary:
# for each cell instance you create, do the following 
# create subsets e.g. basilars, obliques, apicals
# specify cm and Ra, then spatial discretization via the d_lambda rule
# insert mechanisms
# adjust location-dependent parameters
Vital caveats, especially for engineers, physicists, and mathematicians:
Never assume that morphometric data is "correct," even if your mother gave it to you. Always check for errors. The file names used in the example above are from the Duke-Southampton archive. Those files are full of problems. Don't know what kinds of errors to look for? Work through the Import3d Tutorials (see the link at https://neuron.yale.edu/neuron/docs). See the Hot tips area of the NEURON Forum. Read the relevant literature. Talk to somebody who has expertise in quantitative cellular neuroanatomy. Be sure to look for error messages that NEURON generates when it imports a morphometric data file.

vogdb
Posts: 37
Joined: Sun Aug 13, 2017 9:51 am

Re: Neuron transforms soma that is loaded from .swc. What is the rule for it?

Post by vogdb » Fri Feb 21, 2020 7:12 am

Thank you for the information!
In my case the solution was to use the last point of soma for connection of other sections. So, instead of parent id 1 use 3

Code: Select all

...
4           2     0.292800    -4.265800    -5.562200     0.325200           1
...
Use

Code: Select all

# index     type         X            Y            Z       radius       parent
...
4           2     0.292800    -4.265800    -5.562200     0.325200           3
...
And this is actually the rule for .swc morphologies. Always connect child's start to parent's end.

Post Reply