Adding morphologies¶
Note
This guide is a continuation of the Getting Started guide.
Previously, we constructed a stacked double layer topology, with 2 cell types. We then connected these cell type populations in an all-to-all fashion.
In this tutorial, we are going to assign morphologies to our cells, and connects the cells based on the intersection of their morphologies! You will learn how to load morphologies from local files or to fetch from remote sources, like NeuroMorpho, using the BSB.
neuron_A.swc
and neuron2.swc
in your
project folder.Using local files¶
morphologies is a list component of the BSB configuration responsible to fetch and load morphology files. Here is the minimal configuration example to add a morphology to the scaffold:
morphologies:
- neuron_A.swc
"morphologies": ["neuron_A.swc"]
config.morphologies = ["neuron_A.swc"]
In this case, a morphology is created from neuron_A.swc
and given the name "neuron_A"
.
By default the name assigned to the morphology is the file name without its extension (here .swc
).
Next, we need to associate this morphology to one cell type, here the base_type
, by
referencing it by name in cell_types.base_type.spatial.morphologies:
cell_types:
base_type:
spatial:
radius: 2.5
density: 3.9e-4
morphologies:
- neuron_A
"cell_types": {
"base_type": {
"spatial": {
"radius": 2.5,
"density": 3.9e-4,
"morphologies": ["neuron_A"]
}
},
config.cell_types.add(
"base_type",
spatial=dict(
radius=2.5,
density=3.9e-4,
morphologies=["neuron_A"],
),
)
Note
If there are multiple morphologies per cell type, they will be assigned randomly, unless you specify a MorphologyDistributor.
Let’s add the second morphology but this time we will change its name with a morphology node containing the attributes name and file:
morphologies:
- neuron_A.swc
- name: neuron_B
file: neuron2.swc
"morphologies": [
"neuron_A.swc",
{
"name": "neuron_B",
"file": "neuron2.swc"
}
config.morphologies = [
"neuron_A.swc",
dict(name="neuron_B", file="neuron2.swc"),
]
It is also possible to add a pipeline to perform transformations on the loaded
morphology. Pipelines can be added with a pipeline list component to the
morphology node.
Each item in the list may either be a string reference to a method of the
Morphology
class or an importable function.
If the function requires parameters, use a node with the function reference placed in the
func attribute, and a parameters list.
Here is an example what that would look like:
morphologies:
- file: my_neuron.swc
pipeline:
- center
- my_module.add_axon
- func: rotate
rotation: [20, 0, 20]
"morphologies": [
{
"file": "my_neuron.swc",
"pipeline": [
"center",
"my_module.add_axon",
{
"func": "rotate",
"rotation": [20, 0, 20]
},
],
}
]
config.morphologies = [
dict(
file= "my_neuron.swc",
pipeline=[
"center",
"my_module.add_axon",
dict(func="rotate", rotation=[20, 0, 20])
]
)
]
In this case, we created a pipeline of 3 steps:
Reset the origin of the morphology, using the
center()
function from the Morphology class.Run the add_axon function from the external file my_module.py
Rotate the morphology by 20 degrees along the x and z axis, using the
rotate()
function from the Morphology class.
Note
Any additional keys given in a pipeline step, such as rotation in the example, are passed to the function as keyword arguments.
Morphology intersection¶
Now that we have assigned morphologies to our cell types, we can use morphology-based connection strategies such as VoxelIntersection:
connectivity:
A_to_B:
strategy: bsb.connectivity.VoxelIntersection
presynaptic:
cell_types:
- base_type
postsynaptic:
cell_types:
- top_type
"connectivity": {
"A_to_B": {
"strategy": "bsb.connectivity.VoxelIntersection",
"presynaptic": {
"cell_types": ["base_type"]
},
"postsynaptic": {
"cell_types": ["top_type"]
}
}
}
config.connectivity.add(
"A_to_B",
strategy="bsb.connectivity.VoxelIntersection",
presynaptic=dict(cell_types=["base_type"]),
postsynaptic=dict(cell_types=["top_type"]),
)
Note also that with Voxel Intersection, you can specify which parts of the morphologies should create contacts (e.g, dendrites and axons):
"connectivity": {
"A_to_B": {
"strategy": "bsb.connectivity.VoxelIntersection",
"presynaptic": {
"cell_types": ["base_type"],
"morphology_labels": ["axon"]
},
"postsynaptic": {
"cell_types": ["top_type"],
"morphology_labels": ["dendrites"]
}
}
}
This happens thanks to the labels that are attached to your morphology points.
Tip
Do not forget to recompile your network if you are modifying the configuration file.
Final configuration file¶
name: Starting example
storage:
engine: hdf5
root: network.hdf5
network:
x: 200.0
y: 200.0
z: 200.0
morphologies:
- neuron_A.swc
- name: neuron_B
file: neuron2.swc
- name: neron_NM
file: nm://cell005_GroundTruth
regions:
brain_region:
type: stack
children:
- base_layer
- top_layer
partitions:
base_layer:
type: layer
thickness: 100
top_layer:
type: layer
thickness: 100
cell_types:
base_type:
spatial:
radius: 2.5
density: 3.9e-4
morphologies:
- neuron_A
top_type:
spatial:
radius: 7
count: 40
morphologies:
- neuron_B
- neuron_NM
placement:
base_placement:
strategy: bsb.placement.RandomPlacement
cell_types:
- base_type
partitions:
- base_layer
top_placement:
strategy: bsb.placement.RandomPlacement
cell_types:
- top_type
partitions:
- top_layer
connectivity:
A_to_B:
strategy: bsb.connectivity.VoxelIntersection
presynaptic:
cell_types:
- base_type
postsynaptic:
cell_types:
- top_type
{
"name": "Starting example",
"storage": {
"engine": "hdf5",
"root": "network.hdf5"
},
"network": {
"x": 200.0,
"y": 200.0,
"z": 200.0
},
"morphologies": [
"neuron_A.swc",
{
"name": "neuron_B",
"file": "neuron2.swc"
}
],
"regions": {
"brain_region": {
"type": "stack",
"children": ["base_layer", "top_layer"]
}
},
"partitions": {
"base_layer": {
"type": "layer",
"thickness": 100
},
"top_layer": {
"type": "layer",
"thickness": 100
}
},
"cell_types": {
"base_type": {
"spatial": {
"radius": 2.5,
"density": 3.9e-4,
"morphologies": ["neuron_A"]
}
},
"top_type": {
"spatial": {
"radius": 7,
"count": 40,
"morphologies": [
"neuron_B"
]
}
}
},
"placement": {
"base_placement": {
"strategy": "bsb.placement.RandomPlacement",
"cell_types": ["base_type"],
"partitions": ["base_layer"]
},
"top_placement": {
"strategy": "bsb.placement.RandomPlacement",
"cell_types": ["top_type"],
"partitions": ["top_layer"]
}
},
"connectivity": {
"A_to_B": {
"strategy": "bsb.connectivity.VoxelIntersection",
"presynaptic": {
"cell_types": ["base_type"]
},
"postsynaptic": {
"cell_types": ["top_type"]
}
}
}
}
import bsb.options
from bsb import Configuration, Scaffold
bsb.options.verbosity = 3
config = Configuration.default(storage={"engine": "hdf5", "root": "network.hdf5"})
config.network.x = 200.0
config.network.y = 200.0
config.network.z = 200.0
config.partitions.add("base_layer", thickness=100)
config.partitions.add("top_layer", thickness=100)
config.regions.add(
"brain_region",
type="stack",
children=[
"base_layer",
"top_layer",
],
)
config.morphologies = [
"neuron_A.swc",
dict(name="neuron_B", file="neuron2.swc"),
]
config.cell_types.add(
"base_type",
spatial=dict(
radius=2.5,
density=3.9e-4,
morphologies=["neuron_A"],
),
)
config.cell_types.add(
"top_type",
spatial=dict(
radius=7,
count=10,
morphologies=["neuron_B"],
),
)
config.placement.add(
"base_placement",
strategy="bsb.placement.RandomPlacement",
cell_types=["base_type"],
partitions=["base_layer"],
)
config.placement.add(
"top_placement",
strategy="bsb.placement.RandomPlacement",
cell_types=["top_type"],
partitions=["top_layer"],
)
config.connectivity.add(
"A_to_B",
strategy="bsb.connectivity.VoxelIntersection",
presynaptic=dict(cell_types=["base_type"]),
postsynaptic=dict(cell_types=["top_type"]),
)
scaffold = Scaffold(config)
scaffold.compile(clear=True)
What is next?¶
Next tutorial is on running a simulation of your network.