from tvbo import Dynamics, SimulationExperiment
model = Dynamics.from_db("ReducedWongWangExcInh")
exp = SimulationExperiment(dynamics=model)
result = exp.run("jax")Working with Results
Accessing simulation output, observations, and exporting to BIDS
Overview
After running a simulation experiment, TVBO returns an ExperimentResult that provides structured access to all output data — raw state variables, derived observations (BOLD, FC, PSD), algorithm history, and optimization traces.
The result object mirrors the YAML experiment specification: each section of the experiment (integration, algorithms, optimizations, explorations) maps to a corresponding field on the result.
Result Architecture
ExperimentResult
├── name # Experiment label
├── source # Back-reference to SimulationExperiment
├── integration # SimulationResult — main simulation
│ ├── data # xr.DataArray (time × variable × node [× mode])
│ ├── observations # dict of observation outputs (BOLD, FC, …)
│ └── transient # SimulationResult — warm-up (if any)
├── algorithms # dict[str, AlgorithmResult]
│ └── fic # e.g. FIC tuning result
│ ├── state # Final tuned state
│ ├── history # Per-iteration tracking
│ ├── pre_tuning # SimulationResult before algorithm
│ └── post_tuning # SimulationResult after algorithm
├── optimizations # dict[str, OptimizationResult]
│ └── loss_fc # e.g. FC-based optimization
│ ├── state # Fitted parameters
│ ├── history # Loss trajectory
│ └── simulation # SimulationResult with fitted params
├── explorations # dict[str, ExplorationResult]
│ └── grid # Parameter sweep results
└── continuations # dict — bifurcation analysis results
Basic Usage
The ExperimentResult displays a tree summary:
resultExperiment
└── integration
data: (81920, 2, 1, 1)
Accessing Data
Integration (Main Simulation)
The primary simulation result is an xr.DataArray with named dimensions and coordinates:
result.integration.data<xarray.DataArray (time: 81920, variable: 2, node: 1, mode: 1)> Size: 1MB
array([[[[0.09998933]],
[[0.09988083]]],
[[[0.09997866]],
[[0.09976182]]],
[[[0.099968 ]],
[[0.09964298]]],
...,
[[[0.16456527]],
[[0.03920144]]],
[[[0.16456528]],
[[0.03920144]]],
[[[0.16456529]],
[[0.03920144]]]], shape=(81920, 2, 1, 1))
Coordinates:
* time (time) float64 655kB 0.01221 0.02441 0.03662 ... 1e+03 1e+03 1e+03
* variable (variable) <U3 24B 'S_e' 'S_i'
* mode (mode) int64 8B 0
Dimensions without coordinates: nodeUse xarray’s label-based selection:
# Select a single state variable across all nodes
result.integration.sel(variable='S_e')SimulationResult(81920, 1, 1)
# Integer indexing — first 1000 time steps
result.integration.isel(time=slice(0, 1000))SimulationResult(1000, 2, 1, 1)
Standard properties remain available:
print("state_names:", result.integration.state_names)
print("dims: ", result.integration.data.dims)
print("shape: ", result.integration.data.shape)state_names: [np.str_('S_e'), np.str_('S_i')]
dims: ('time', 'variable', 'node', 'mode')
shape: (81920, 2, 1, 1)
Backward Compatibility
ExperimentResult delegates to integration for backward compatibility. This means result.data and result.time work as before:
# These are equivalent:
assert result.data is result.integration.data
print("result.data.shape:", result.data.shape)result.data.shape: (81920, 2, 1, 1)
Source Experiment Link
ExperimentResult holds a back-reference to the SimulationExperiment that produced it:
print("source type: ", type(result.source).__name__)
print("dynamics name:", result.source.dynamics.name)source type: SimulationExperiment
dynamics name: ReducedWongWangExcInh
Observations
Observations (BOLD, functional connectivity, PSD, etc.) are attached to the simulation that produced them:
result.integration.observations # dict of observation outputs
result.integration.observations['bold'] # BOLD SimulationResult
result.integration.observations['bold'].data # xr.DataArrayAlgorithm Results
When running experiments with algorithms (e.g. FIC, EIB), results track the full iteration history:
# Load and run an experiment with algorithms
exp = SimulationExperiment.from_db("EI_Tuning_FIC_EIB_Optimization")
result = exp.run("tvboptim")
fic = result.algorithms['fic']
fic.name # 'fic'
fic.n_iterations # 200
fic.state # Final tuned state (parameter arrays)
fic.history # Per-iteration tracking
fic.pre_tuning # SimulationResult before tuning
fic.post_tuning # SimulationResult after tuning
fic.convergence # Computed convergence metricsOptimization Results
Optimization results track loss trajectory and parameter evolution:
opt = result.optimizations['gradient_eib']
opt.name # 'gradient_eib'
opt.n_steps # Number of gradient steps
opt.final_loss # Final loss value
opt.loss_trajectory # Loss at each step (array)
opt.state # Fitted parameters
opt.simulation # Post-optimization SimulationResultExporting Results
BIDS-Compatible Export
ExperimentResult.export() writes simulation data and experiment metadata to a BIDS-compatible directory following BEP034 conventions (computational model derivatives):
output_dir/
├── dataset_description.json
└── sub-{subject}/
├── sub-{subject}_desc-{desc}_experiment.yaml # full LinkML experiment spec
└── ts/
├── sub-{subject}_desc-{desc}_ts-sim_State.nc # raw state variables
├── sub-{subject}_desc-{desc}_ts-sim_State.json # sidecar metadata
└── sub-{subject}_desc-{desc}_ts-{obs}.nc # one per observation
import tempfile, os
outdir = os.path.join(tempfile.mkdtemp(), "my_experiment")
result.export(outdir)PosixPath('/var/folders/ym/9kw1g21j1nd7kwfn8c0z3st40000gn/T/tmp9kiybybe/my_experiment')
# Show what was written
for root, dirs, files in os.walk(outdir):
level = root.replace(outdir, "").count(os.sep)
indent = " " * level
print(f"{indent}{os.path.basename(root)}/")
for f in sorted(files):
print(f" {indent}{f}")my_experiment/
dataset_description.json
sub-01/
sub-01_desc-tvbsim_experiment.yaml
ts/
sub-01_desc-tvbsim_ts-sim_State.json
sub-01_desc-tvbsim_ts-sim_State.nc
Export Options
result.export(
"output/",
subject="02", # BIDS subject label (default: "01")
session="pre", # Optional BIDS session
description="fic_tuned", # desc- entity (default: "tvbsim")
)What Gets Written
| Component | File | Format |
|---|---|---|
| Experiment specification | *_experiment.yaml |
YAML (LinkML) |
| Simulation data | *_ts-sim_State.nc |
netCDF (self-describing, HDF5-based) |
| Observations | *_ts-{name}.nc |
netCDF per observation |
| Algorithm post-tuning | *_ts-{algo}_State.nc |
netCDF |
| Optimization simulation | *_ts-{opt}_State.nc |
netCDF |
| Sidecar metadata | *_State.json |
JSON (shape, dims, sample period) |
| Dataset description | dataset_description.json |
JSON (BIDS required) |
Data Format: netCDF
Simulation data is stored as netCDF4 (.nc) — a self-describing scientific data format built on HDF5. Each file contains the full array with named dimensions and coordinates, readable by any netCDF or HDF5 tool.
Why netCDF and not NIfTI or plain HDF5?
| Format | Suitable? | Reason |
|---|---|---|
| NIfTI | No | Designed for volumetric (voxel) imaging. Region-by-region time series (time × variable × node × mode) don’t fit the NIfTI data model and we’d lose labelled axes. |
| Plain HDF5 | Partially | HDF5 has no built-in concept of named dimensions or coordinate variables; we’d need to invent our own conventions. |
| netCDF4 | Yes | Self-describing dimensions + coordinates and HDF5-compatible storage. Round-trips xarray DataArrays losslessly. |
netCDF4 files are HDF5 files. Any HDF5 reader (h5py, HDFView, MATLAB’s h5read) can open them directly. The netCDF layer just adds self-describing dimension names and coordinates on top.
Reading exported data back
import xarray as xr
nc_files = [f for f in os.listdir(os.path.join(outdir, "sub-01", "ts")) if f.endswith(".nc")]
nc_path = os.path.join(outdir, "sub-01", "ts", nc_files[0])
ds = xr.open_dataset(nc_path)
ds['data']<xarray.DataArray 'data' (time: 81920, variable: 2, node: 1, mode: 1)> Size: 1MB [163840 values with dtype=float64] Coordinates: * time (time) float64 655kB 0.01221 0.02441 0.03662 ... 1e+03 1e+03 1e+03 * variable (variable) object 16B 'S_e' 'S_i' * mode (mode) int32 4B 0 Dimensions without coordinates: node
# Clean up
import shutil
shutil.rmtree(os.path.dirname(outdir))See Also
- Simulation Experiments — defining and running experiments
- Observation Models — derived observables (BOLD, FC, …)
- Visualization — detailed plotting guide