Run your first NEST simulation

Note

This guide is a continuation of the Getting Started guide.

Install requirements

NEST is one of the supported simulators of the BSB. As for the other simulator, its adapter code is stored in a separate repository: bsb-neuron

So, you would need to install it with pip:

pip install bsb-nest

Unfortunately, the NEST simulator at the moment can not be installed directly by pip, but fortunately NEST provides tutorials to install it in your python environment.

Make sure that you can both load BSB and NEST before continuing any further:

import nest
import bsb

Configuration of the simulation

In this tutorial, we assume that you have successfully reconstructed a network with BSB. We will now guide you through the process of configuring a simulation with BSB for your network.

We want here to put the circuit reconstructed in a steady state with a low basal activity.

Let’s start by configuring the global simulation parameters. These include the simulator to be used; in our example, we are setting it to use NEST. Additionally, you need to define the resolution (the time step of the simulation in milliseconds) and the duration (the total length of the simulation in milliseconds). Therefore, your simulation block should be structured as follows:

"simulations": {
    "basal_activity": {
      "simulator": "nest",
      "resolution": 0.1,
      "duration": 5000,
      "cell_models": {
      },
      "connection_models": {
      },
      "devices":{
      }
}
config.simulations.add("basal_activity",
  simulator="nest",
  resolution=0.1,
  duration=5000,
  cell_models={},
  connection_models={},
  devices={}
)

Note

If you are using Python code, we assume that you load your Scaffold and Configuration from your compiled network file:

scaffold = from_storage("network.hdf5")
config = scaffold.configuration

Cells Models

For each cell type population of your network, you will need to assign a point neuron model to determine how these cells will behave during the simulation (i.e., their inner equations). The keys given in the cell_models should correspond to one of the cell_types of your configuration.

Note

If a certain cell_type does not have a corresponding cell_model then no cells of that type will be instantiated in the network.

Here, we choose one of the simplest NEST models, the Integrate-and-Fire neuron model:

"cell_models": {
   "base_type": {
     "model": "iaf_cond_alpha"
   },
   "top_type": {
     "model": "iaf_cond_alpha"
   }
 },
config.simulations["basal_activity"].cell_models=dict(
  base_type={"model":"iaf_cond_alpha"},
  top_type={"model":"iaf_cond_alpha"}
)

NEST provides default parameters for each point neuron model, so we do not need to add anything. Still, you can modify certain parameters, by setting its constants dictionary:

"cell_models": {
  "base_type": {
    "model": "iaf_cond_alpha",
    "constants": {
      "t_ref": 1.5,
      "V_m": -62.0
    }
  },
config.simulations["basal_activity"].cell_models=dict(
  base_type={"model":"iaf_cond_alpha", dict(t_ref=1.5, V_m=-62.0)},
)

Connection Models

For each connection type of your network, you also need to define a model describing its synapses’ dynamics. Similar to the cell_models block, for each connection_model you should use a key that corresponds to a ConnectivitySet created during reconstruction (as explained in the previous section). In this example, we assign the static_synapse model to the connections A_to_B.

"connection_models": {
  "A_to_B": {
      "synapse": {
        "model": "static_synapse",
        "weight": 100,
        "delay": 1
      }
  }
},
config.simulations["basal_activity"].connection_models=dict(
  A_to_B=dict(
    synapse=dict(
      model="static_synapse",
      weight=100,
      delay=1
    )
  )
)

For this model, the synapse model needs weight and delay parameters that are set to 100 and 1 ms, respectively.

Devices

In the devices block, include all interfaces you wish to use for interacting with the network. These devices correspond typically to stimulators and measurement instruments.

Use the device key to select the type of device. We also introduce here the targetting concept for the devices: This configuration node allows you to filter elements of your neuron circuit to which you want to link your devices (see the targetting section on this page for more details).

"devices": {
        "background_noise": {
          "device": "poisson_generator",
          "rate": 20,
          "targetting": {
            "strategy": "cell_model",
            "cell_models": ["base_type"]
          },
          "weight": 40,
          "delay": 1
        },
        "base_layer_record": {
          "device": "spike_recorder",
          "delay": 0.1,
          "targetting": {
            "strategy": "cell_model",
            "cell_models": ["base_type"]
          }
        },
        "top_layer_record": {
          "device": "spike_recorder",
          "delay": 0.1,
          "targetting": {
            "strategy": "cell_model",
            "cell_models": ["top_type"]
          }
        }
}
config.simulations["basal_activity"].devices=dict(
  general_noise=dict(
          device= "poisson_generator",
          rate= 20,
          targetting= {
            "strategy": "cell_model",
            "cell_models": ["base_type"]
          },
          weight= 40,
          delay= 1
  ),
  base_layer_record=dict(
          device= "spike_recorder",
          delay= 0.1,
          targetting= {
            "strategy": "cell_model",
            "cell_models": ["base_type"]
          }
  ),
  top_layer_record=dict(
          device= "spike_recorder",
          delay= 0.1,
          targetting= {
            "strategy": "cell_model",
            "cell_models": ["top_type"]
          }
  )
)

In our example, we add a poisson_generator that simulates cells spiking at 20 Hz. These latter “cells” are each connected one top_type cell and transmit their spike events with a delay of 1 ms and the weight of the connection is 40. We also introduce a spike_recorder to store the spike events of the cell populations.

Final configuration file

name: Starting example
storage:
  engine: hdf5
  root: network.hdf5
network:
  x: 200
  y: 200
  z: 200
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
simulations:
  basal_activity:
    simulator: nest
    resolution: 0.1
    duration: 5000
    cell_models:
      base_type:
        model: iaf_cond_alpha
      top_type:
        model: iaf_cond_alpha
        constants:
          t_ref: 1.5,
          V_m: -62.0
    connection_models:
      A_to_B:
        synapse:
          model: static_synapse
          weight: 300
          delay: 1
    devices:
      background_noise:
        device: poisson_generator
        rate: 10
        targetting:
          strategy: cell_model
          cell_models:
          - base_type
        weight: 40
        delay: 10
      base_layer_record:
        device: spike_recorder
        delay: 0.1
        targetting:
          strategy: cell_model
          cell_models:
          - base_type
      top_layer_record:
        device: spike_recorder
        delay: 0.1
        targetting:
          strategy: cell_model
          cell_models:
          - top_type
{
  "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"]
      }
    }
  },
  "simulations": {
    "basal_activity": {
      "simulator": "nest",
      "resolution": 0.1,
      "duration": 5000,
      "cell_models": {
        "base_type": {
          "model": "iaf_cond_alpha"
        },
        "top_type": {
          "model": "iaf_cond_alpha",
          "constants": {
            "t_ref": 1.5,
            "V_m": -62.0
          }
        }
      },
      "connection_models": {
        "A_to_B": {
          "synapse": {
            "model": "static_synapse",
            "weight": 300,
            "delay": 1
          }
        }
      },
      "devices": {
        "background_noise": {
          "device": "poisson_generator",
          "rate": 10,
          "targetting": {
            "strategy": "cell_model",
            "cell_models": [
              "base_type"
            ]
          },
          "weight": 40,
          "delay": 10
        },
        "base_layer_record": {
          "device": "spike_recorder",
          "delay": 0.1,
          "targetting": {
            "strategy": "cell_model",
            "cell_models": [
              "base_type"
            ]
          }
        },
        "top_layer_record": {
          "device": "spike_recorder",
          "delay": 0.1,
          "targetting": {
            "strategy": "cell_model",
            "cell_models": [
              "top_type"
            ]
          }
        }
      }
    }
  }
}
import bsb.options
from bsb import from_storage

bsb.options.verbosity = 3

scaffold = from_storage("network.hdf5")
config = scaffold.configuration

config.simulations.add(
    "basal_activity",
    simulator="nest",
    resolution=0.1,
    duration=5000,
    cell_models={},
    connection_models={},
    devices={},
)
config.simulations["basal_activity"].cell_models = dict(
    base_type={"model": "iaf_cond_alpha"},
    top_type={"model": "iaf_cond_alpha", "constants": {"t_ref": 1.5, "V_m": -62.0}},
)

config.simulations["basal_activity"].connection_models = dict(
    A_to_B=dict(synapse=dict(model="static_synapse", weight=100, delay=1))
)

config.simulations["basal_activity"].devices = dict(
    general_noise=dict(
        device="poisson_generator",
        rate=20,
        targetting={"strategy": "cell_model", "cell_models": ["base_type"]},
        weight=40,
        delay=1,
    ),
    base_layer_record=dict(
        device="spike_recorder",
        delay=0.1,
        targetting={"strategy": "cell_model", "cell_models": ["base_type"]},
    ),
    top_layer_record=dict(
        device="spike_recorder",
        delay=0.1,
        targetting={"strategy": "cell_model", "cell_models": ["top_type"]},
    ),
)

Running the Simulation

Simulations are separated from the reconstruction pipeline (see the top level guide), which means you do not need to recompile your network to add a simulation to your stored Configuration. In this example, we only modified the Configuration in the simulations block but this updates were not been saved in the network file. So, you need to update your file, using either the reconfigure command or the store_active_config method.

bsb reconfigure network.hdf5 network_configuration.json
storage = scaffold.storage
storage.store_active_config(config)

Once this is done, create a folder in which to store your simulation results:

mkdir simulation-results

You can now run your simulation:

bsb simulate network.hdf5 basal_activity -o simulation-results
from bsb import from_storage

scaffold = from_storage("network.hdf5")
result = scaffold.run_simulation("basal_activity")
result.write("simulation-results/basal_activity.nio", "ow")

The results of the simulation will be stored in the "simulation-results" folder.

Note

If you run the simulation with the command line interface, the name of the output nio file is randomized by the BSB.

For more detailed information about simulation modules, please refer to the simulation section.

Congratulations, you simulated your first BSB reconstructed network with NEST!

Next steps:

Analyze your Results

How to extract your data.

Make custom components

Learn how to write your own components to e.g. place or connect cells.

Learn about components

Explore more about the main components.

Examples

Explore more advanced examples