Your first network

Make sure you completed the Installation guide before running the example in this section.

Note

This guide aims to get your first model running with the minimum number of steps.
If you would like to familiarize yourself with the core concepts and get a more top level understanding first, check out the Configuration files before you continue.

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 and connectome.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.