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:
import tempfile, os
outdir = os.path.join(tempfile.mkdtemp(), "my_experiment")
result.export(outdir)PosixPath('/var/folders/ym/9kw1g21j1nd7kwfn8c0z3st40000gn/T/tmprnqnjnsa/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 netCDF — a self-describing scientific data format built on HDF5. Each file contains the full array with named dimensions and coordinates, readable by any netCDF/HDF5 tool:
import xarray as xr
# Read back exported data
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
netCDF4 files are HDF5 files — any HDF5 reader (h5py, HDFView, MATLAB’s h5read) can open them directly. The netCDF layer adds self-describing dimension names and coordinates.
# 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