Your first network¶
Make sure you completed the Installation guide before running the example in this section.
Note
The framework supports both declarative statements in configuration formats, or Python code. Be sure to take a quick look at each code tab to get a feel for the equivalent forms of configuration coding!
Create a project¶
Use the command below to create a new BSB project directory and some starter files:
bsb new my_first_model --quickstart --json
cd my_first_model
Projects help you keep your models organized, safe, and neat! Your folder should contain:
network_configuration.json
: your configuration file. The components are declared and parametrized here.A
pyproject.toml
file: This file uses the TOML syntax to set configuration values for the BSB and any other python tools your project uses.A
placement.py
andconnectome.py
files if you want to make your own components.
Python project settings are contained in the pyproject.toml
file.
A lot of Python options can be configured with your toml
file such as the python
libraries necessary to deploy it. If you want to learn more about this configuration tool,
check out this tutorial and the
bsb related options.
The configuration contains already a partition base_layer
, a cell_type
base_type
and a placement strategy example_placement
.
These minimal components are enough to compile your first network. You can do this from the terminal
or with Python:
bsb compile --verbosity 3
import bsb.options
from bsb import Scaffold, parse_configuration_file
bsb.options.verbosity = 3
config = parse_configuration_file("network_configuration.json", parser="json")
scaffold = Scaffold(config)
scaffold.compile()
Here, the verbosity
flag increases the amount of output (logs) that is generated when the BSB is
running, to follow along or troubleshoot.
When the BSB compiles a Scaffold, it extracts and runs the reconstruction pipeline defined in the Configuration and stores each step’s results into the Storage (as explained in the previous section).
The compile command (or python script) should produce a file "network.hdf5"
located in your project
folder if the BSB could parse the configuration file and complete the reconstruction. This file should
contain your network (configuration and storage) after reconstruction.
Note
The configuration file can be written in either json
or yaml
format;
By default, the new command uses the yaml format unless the --json
flag is set.
If you prefer, instead of loading the configuration from a file, you can create your configuration
directly in Python code with a Configuration
object:
import bsb.options from bsb import Scaffold, Configuration bsb.options.verbosity = 3 config = Configuration.default(storage=dict(engine="hdf5", root="network.hdf5")) # Implement your code here scaffold = Scaffold(config) scaffold.compile()
Define starter components¶
Network¶
The network
component describes the global spatial properties of your circuit,
including its size along the three dimensions x, y, z
(in µm).
"network": {
"x": 200.0,
"y": 200.0,
"z": 200.0
},
config.network.x = 200.0
config.network.y = 200.0
config.network.z = 200.0
Topology¶
Your network model needs a description of its shape, which is called the topology of the network. The topology consists of 2 components: Regions and Partitions. Regions combine multiple partitions and/or regions together, in a hierarchy, all the way up to a single topmost region, while partitions are exact pieces of volume that can be filled with cells.
To get started, we will add a second layer top_layer
, and a region brain_region
:
"regions": {
"brain_region": {
"type": "stack",
"children": ["base_layer", "top_layer"]
}
},
"partitions": {
"base_layer": {
"type": "layer",
"thickness": 100
},
"top_layer": {
"type": "layer",
"thickness": 100
}
},
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",
],
)
The type of the brain_region
is stack
. This means it will place its
children stacked on top of each other. The type of base_layer
is
layer
. Layers specify their size in one dimension, and fill up the space in the other
dimensions. See the topology section for more explanation on
these components.
Warning
The BSB checks the configuration for errors each time the latter is modified. Now, in the Python code implementation, we are adding components one by one. This means that if one component refers to another, this latter should already in the configuration. That is why, in the python code implementation, we created the partitions before the region because the region uses references to the partitions’ name.
Cell types¶
The Cell Types define populations of cells.
In the simplest case, you can define a cell type
by its soma radius and
the number of cells to place using either a density value, or a fixed
count, or another
placement indication.
To populate our new top_layer
, we will create an extra cell type top_type
; this
time we want to a place 40 of these cells and their soma radius of 7
.
"cell_types": {
"base_type": {
"spatial": {
"radius": 2.5,
"density": 3.9e-4
}
},
"top_type": {
"spatial": {
"radius": 7,
"count": 40
}
}
},
config.cell_types.add(
"base_type",
spatial=dict(
radius=2.5,
density=3.9e-4,
),
)
config.cell_types.add("top_type", spatial=dict(radius=7, count=40))
Placement¶
The placement blocks are in charge of placing cells in the partitions using the cell type indications. For each placement component, you should specify the placement strategy to use, the list of cell_types names to place and the list of partitions in which you want the placement to happen.
Now that we have defined our new top_type
, we should place it in our top_layer
:
"placement": {
"example_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"]
}
},
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"],
)
We added here the top_placement
strategy.
top_placement
and example_placement
are both following a RandomPlacement
strategy from the BSB, which assigns a random position to the cells’ soma within their
respective partition.
You should now try to compile your network to check if you did no mistake:
bsb compile -v 3 --clear
# bsb.options.verbosity = 3 # if not set previously
scaffold.compile(clear=True)
Note
We are using the short forms -v
of the CLI options verbosity
.
You can use bsb --help
to inspect the CLI options.
Warning
We pass the clear
flag to indicate that existing data may be overwritten. See
Storage flags for more flags to deal with existing data.
Each placement strategy generates a PlacementSet in the Storage that you can access from the Scaffold object (see this section for more info).
Connectivity¶
The connectivity component contains the blocks that specify connections between systems of cell types. For each connectivity component, you should specify the connection strategy and for both presynaptic (source) and postsynaptic (target) groups, provide the list of cell_types names to connect.
Here, we are going to connect all base_type
cells to all top_type
cells.
"connectivity": {
"A_to_B": {
"strategy": "bsb.connectivity.AllToAll",
"presynaptic": {
"cell_types": ["base_type"]
},
"postsynaptic": {
"cell_types": ["top_type"]
}
}
}
}
config.connectivity.add(
"A_to_B",
strategy="bsb.connectivity.AllToAll",
presynaptic=dict(cell_types=["base_type"]),
postsynaptic=dict(cell_types=["top_type"]),
)
Recompile the network once more, now it will also contain your connections! With your cells and connections in place, you are ready to move to the next stage.
Note
For Python, the compile function should be called (only once) at the end of your script, once the configuration is complete.
Each connection strategy generates a ConnectivitySet in the Storage for each pair of cell types
that you can access from the Scaffold object (see this section for more info).
Here, the name of the ConnectivitySet corresponds to the connection component (A_to_B
) because
there is only one pair of cell_type.
Warning
If you have more than one pair of cell types connected through the same connection strategy, then the name of
the ConnectivitySet is NameOfTheComponent
_ NameOfPreType
_ NameOfPostType
(learn more here).
Final configuration file¶
{
"name": "Starting example",
"storage": {
"engine": "hdf5",
"root": "network.hdf5"
},
"network": {
"x": 200.0,
"y": 200.0,
"z": 200.0
},
"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
}
},
"top_type": {
"spatial": {
"radius": 7,
"count": 40
}
}
},
"placement": {
"example_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.AllToAll",
"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.cell_types.add(
"base_type",
spatial=dict(
radius=2.5,
density=3.9e-4,
),
)
config.cell_types.add("top_type", spatial=dict(radius=7, count=40))
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.AllToAll",
presynaptic=dict(cell_types=["base_type"]),
postsynaptic=dict(cell_types=["top_type"]),
)
scaffold = Scaffold(config)
scaffold.compile(clear=True)
What is next?¶
Learn how to extract the data from your produced Scaffold through this tutorial.