Simulation¶
The simulation engine runs finite element method (FEM) calculations using SimNIBS to compute temporal interference electric fields in the brain. TI-Toolbox supports two simulation modes: TI (2-pair) and mTI (4+ pairs, even).
graph LR
MESH([Head Mesh]) --> SIM[SimNIBS FEM]
MONT([Montage Config]) --> SIM
INT([Intensity Config]) --> SIM
SIM --> TI_MAX([TI_max Field])
SIM --> TI_NORM([TI_normal Field])
SIM --> NIFTI([NIfTI Volumes])
style MESH fill:#1a3a5c,stroke:#48a,color:#fff
style MONT fill:#1a3a5c,stroke:#48a,color:#fff
style INT fill:#1a3a5c,stroke:#48a,color:#fff
style SIM fill:#2d5a27,stroke:#4a8,color:#fff
style TI_MAX fill:#1a5c4a,stroke:#4a8,color:#fff
style TI_NORM fill:#1a5c4a,stroke:#4a8,color:#fff
style NIFTI fill:#1a5c4a,stroke:#4a8,color:#fff
Running a Simulation¶
from tit.sim import (
SimulationConfig, Montage,
run_simulation, load_montages,
)
# Load montages from the project's montage_list.json
montages = load_montages(
montage_names=["motor_cortex"],
eeg_net="GSN-HydroCel-185",
)
# Configure the simulation (montages are part of the config)
config = SimulationConfig(
subject_id="001",
montages=montages,
conductivity="scalar",
intensities=[1.0, 1.0],
electrode_shape="ellipse",
electrode_dimensions=[8.0, 8.0],
gel_thickness=4.0,
)
# Run (auto-detects TI vs mTI based on number of electrode pairs)
results = run_simulation(config)
Simulation Modes¶
The simulation mode is auto-detected from the montage configuration:
| Mode | Electrode Pairs | Description |
|---|---|---|
| TI | 2 pairs | Standard temporal interference — two pairs of electrodes at slightly different frequencies |
| mTI | 4+ pairs (even) | Multi-channel TI — N electrode pairs combined via binary-tree algorithm |
Auto-Detection
You do not need to specify the mode manually. TI-Toolbox inspects the montage: 2 pairs triggers TI mode, 4 or more pairs (even) triggers mTI mode.
Montage Configuration¶
Montages define which electrodes form each pair. They are stored in montage_list.json within your project:
{
"nets": {
"GSN-HydroCel-185": {
"uni_polar_montages": {
"motor_cortex": [["E36", "E224"], ["E104", "E148"]]
}
}
}
}
Load montages programmatically:
from tit.sim import load_montages
montages = load_montages(
montage_names=["motor_cortex", "frontal_target"],
eeg_net="GSN-HydroCel-185",
)
Conductivity Types¶
The conductivity field on SimulationConfig accepts one of four string values:
| Value | Description |
|---|---|
"scalar" |
Isotropic, piecewise-constant conductivity (default, faster) |
"vn" |
Volume-normalized anisotropic conductivity from DTI data |
"dir" |
Direct linear rescaling of diffusion tensor eigenvalues |
"mc" |
Mean-conductivity (isotropic but spatially varying, from DTI) |
Electrode Configuration¶
Electrode parameters are flat fields on SimulationConfig:
config = SimulationConfig(
...,
electrode_shape="ellipse", # "ellipse" or "rect"
electrode_dimensions=[8.0, 8.0], # [width, height] in mm
gel_thickness=4.0, # gel layer thickness in mm
rubber_thickness=2.0, # rubber layer thickness in mm
)
Output Fields¶
After simulation, TI-Toolbox produces several derived field types:
| Field | Description |
|---|---|
| TI_max | Maximum TI envelope magnitude — scalar field on volume elements (TI mode) |
| TI_normal | TI field component normal to cortical surface — node field on surface overlay (TI mode) |
| TI_Max | Maximum mTI envelope magnitude — scalar field on volume elements (mTI mode) |
| TI_vectors | Intermediate pairwise TI vector fields for each adjacent pair (mTI mode) |
Outputs are saved as both mesh files (for surface analysis) and NIfTI volumes (for voxel analysis).
Output Directory Structure¶
derivatives/SimNIBS/sub-001/Simulations/
└── motor_cortex/
├── high_Frequency/
│ ├── mesh/ # Per-pair HF mesh files
│ ├── niftis/ # Per-pair HF NIfTI volumes
│ └── analysis/ # fields_summary.txt
├── TI/
│ ├── mesh/ # TI_max and GM/WM mesh files
│ ├── niftis/ # TI NIfTI volumes (subject + MNI space)
│ ├── surface_overlays/ # Cortical surface overlays (for TI_normal)
│ └── montage_imgs/ # Electrode placement visualizations
├── mTI/ # (mTI mode only)
│ ├── mesh/ # mTI_max and intermediate TI meshes
│ ├── niftis/ # mTI NIfTI volumes
│ └── montage_imgs/ # Electrode placement visualizations
└── documentation/ # config.json, SimNIBS logs
API Reference¶
tit.sim.config.SimulationConfig
dataclass
¶
SimulationConfig(subject_id: str, montages: list[Montage], conductivity: str = 'scalar', intensities: list[float] = (lambda: [1.0, 1.0])(), electrode_shape: str = 'ellipse', electrode_dimensions: list[float] = (lambda: [8.0, 8.0])(), gel_thickness: float = 4.0, rubber_thickness: float = 2.0, map_to_surf: bool = True, map_to_vol: bool = False, map_to_mni: bool = False, map_to_fsavg: bool = False, open_in_gmsh: bool = False, tissues_in_niftis: str = 'all', aniso_maxratio: float = 10.0, aniso_maxcond: float = 2.0)
Full configuration for a TI or mTI simulation run.
Passed to :func:tit.sim.run_simulation to execute one or more
montage simulations for a single subject. Electrode geometry,
conductivity model, and output mapping options are all set here.
Attributes¶
subject_id : str
Subject identifier (e.g. "sub-001").
montages : list[Montage]
One or more :class:Montage definitions to simulate.
conductivity : str
Tissue conductivity model. One of:
- ``"scalar"`` -- isotropic scalar conductivities (default).
- ``"vn"`` -- volume-normalized anisotropic conductivities.
- ``"dir"`` -- directly-mapped anisotropic conductivities.
- ``"mc"`` -- mean-conductivity anisotropic conductivities.
The anisotropic modes (``"vn"``, ``"dir"``, ``"mc"``) require
DTI tensors registered to the head mesh.
intensities : list[float]
Per-pair current intensities in mA. Length must be 1 (broadcast
to all pairs) or match the total number of electrode pairs.
Defaults to [1.0, 1.0].
electrode_shape : str
Electrode shape ("ellipse" or "rect").
electrode_dimensions : list[float]
[width, height] of each electrode in mm.
gel_thickness : float
Conductive-gel layer thickness in mm.
rubber_thickness : float
Rubber (silicone) layer thickness in mm.
map_to_surf : bool
Map results onto the cortical surface. Must be True because
TI_normal calculation requires surface overlays.
map_to_vol : bool
Reserved for NIfTI output (handled externally by
tit.tools.mesh2nii, not by SimNIBS SESSION).
map_to_mni : bool
Reserved; not currently passed to SimNIBS.
map_to_fsavg : bool
Reserved; not currently passed to SimNIBS.
open_in_gmsh : bool
Open results in Gmsh after simulation.
tissues_in_niftis : str
Tissue selection for NIfTI export ("all" or a
comma-separated list).
aniso_maxratio : float
Maximum eigenvalue ratio clamp for anisotropic conductivity
tensors.
aniso_maxcond : float
Maximum absolute conductivity clamp (S/m) for anisotropic
tensors.
Raises¶
ValueError If conductivity is not one of the valid model names.
See Also¶
Montage : Electrode montage contained in montages.
run_simulation : Entry point that consumes this config.
parse_intensities : Helper to build the intensities list.
tit.sim.config.Montage
dataclass
¶
Montage(name: str, mode: MontageMode, electrode_pairs: list[tuple], eeg_net: str | None = None)
A named electrode montage used in a TI/mTI simulation.
Wraps the electrode pair definitions for a single montage. Electrodes
may be referenced by EEG-cap label names (NET / FLEX_MAPPED
modes) or by 3-D XYZ coordinates (FLEX_FREE / FREEHAND modes).
The simulation type is auto-detected from the number of electrode pairs: 2 pairs = standard TI, 4+ pairs = multi-channel mTI.
Attributes¶
name : str
Human-readable montage name (e.g. "M1_left").
mode : MontageMode
How electrode positions are specified. See :class:MontageMode.
electrode_pairs : list[tuple]
List of electrode pairs. Each element is a tuple of two electrode
identifiers (label strings or XYZ coordinate lists).
eeg_net : str or None
Filename of the EEG-net CSV (e.g. "GSN-HydroCel-185.csv").
Required for NET and FLEX_MAPPED modes, ignored otherwise.
See Also¶
SimulationConfig : Holds one or more Montage instances.
load_montages : Build Montage objects from montage_list.json.
is_xyz
property
¶
is_xyz: bool
Whether electrodes are specified as 3-D XYZ coordinates.
Returns¶
bool
True for FLEX_FREE and FREEHAND modes.
simulation_mode
property
¶
simulation_mode: SimulationMode
tit.sim.config.MontageMode ¶
Bases: Enum
How electrode positions are specified.
NET and FLEX_MAPPED use EEG-cap label names resolved against an
EEG-net CSV. FLEX_FREE and FREEHAND use raw 3-D XYZ
coordinates (no net required).
Attributes¶
NET : str Standard EEG-cap electrode labels. FLEX_MAPPED : str Flex-search result mapped back to EEG-cap labels. FLEX_FREE : str Flex-search result with free XYZ coordinates. FREEHAND : str User-specified XYZ coordinates (manual placement).
tit.sim.config.SimulationMode ¶
tit.sim.config.parse_intensities ¶
Parse a comma-separated intensity string into a list of floats.
A single value is duplicated to form a pair ("2.0" becomes
[2.0, 2.0]). Otherwise the value count must be even so that
each electrode pair receives two intensities.
Parameters¶
s : str
Comma-separated intensity values (e.g. "1.0,2.0").
Returns¶
list[float] List of floats with an even number of elements.
Raises¶
ValueError If the number of values is odd and greater than 1.
See Also¶
SimulationConfig.intensities : Field populated by this function.
Source code in tit/sim/config.py
tit.sim.base.BaseSimulation ¶
BaseSimulation(config: SimulationConfig, montage: Montage, logger)
Bases: ABC
Abstract base class for TI/mTI simulations.
Provides the template-method run pipeline that subclasses
customise by implementing _build_session and _post_process.
Parameters¶
config : SimulationConfig Fully specified simulation configuration. montage : Montage The electrode montage to simulate. logger : logging.Logger Logger instance for status and diagnostic messages.
Attributes¶
config : SimulationConfig
Simulation configuration supplied at construction.
montage : Montage
Electrode montage supplied at construction.
logger : logging.Logger
Logger instance used throughout the pipeline.
pm : tit.paths.PathManager
Singleton path manager for BIDS path resolution.
m2m_dir : str
Absolute path to the subject's m2m_<subject> directory.
See Also¶
TISimulation : Concrete 2-pair TI subclass.
mTISimulation : Concrete N-pair mTI subclass.
run_simulation : Orchestrates BaseSimulation.run across montages.
Source code in tit/sim/base.py
run ¶
Execute the full simulation pipeline for one montage.
This template method orchestrates directory setup, montage visualisation, SimNIBS FEM execution, and subclass-specific post-processing.
Parameters¶
simulation_dir : str Root simulations directory for the subject.
Returns¶
dict
Result dictionary with keys montage_name, montage_type,
status, and output_mesh.
See Also¶
run_simulation : Calls run for each montage in a config.
Source code in tit/sim/base.py
tit.sim.utils.run_simulation ¶
run_simulation(config: SimulationConfig, logger=None, progress_callback: Callable[[int, int, str], None] | None = None) -> list[dict]
Run TI or mTI simulations for every montage in config.
For each montage in config.montages, this function:
- Auto-detects TI (2 pairs) vs mTI (4+ pairs) from the montage.
- Builds a SimNIBS SESSION with electrode geometry and conductivity settings from config.
- Runs the FEM solver to compute electric-field distributions.
- Computes temporal-interference envelope fields (
TI_max,TI_normal) and, for mTI, the multi-channel superposition. - Writes output meshes, surface overlays, and NIfTIs to the BIDS-compliant simulation directory.
Montages are processed sequentially. If no logger is provided, a file logger is created under the subject's log directory.
Parameters¶
config : SimulationConfig
Full simulation configuration including subject ID, montage
list, electrode geometry, and conductivity model.
logger : logging.Logger or None, optional
Logger instance. If None, a file logger is created
automatically in the subject's BIDS logs directory.
progress_callback : callable or None, optional
Optional callback invoked before each montage as
callback(current_index, total, montage_name) and once more
with (total, total, "Complete") when finished.
Returns¶
list[dict]
One result dict per montage with keys montage_name,
montage_type, status, and output_mesh.
See Also¶
SimulationConfig : The configuration consumed by this function. BaseSimulation.run : Per-montage pipeline called internally. TISimulation : Concrete class for 2-pair simulations. mTISimulation : Concrete class for N-pair simulations.
Source code in tit/sim/utils.py
tit.sim.utils.load_montages ¶
Load named montages from the project's montage_list.json.
Reads the montage_list.json file (managed by
:func:ensure_montage_file), looks up each name under the given
EEG net's uni- and multi-polar sections, and returns them as
:class:Montage instances. When include_flex is True, any
flex/freehand montages found via :func:load_flex_montages are
appended.
The eeg_net value determines the montage mode:
"freehand"setsMontage.Mode.FREEHAND"flex_mode"setsMontage.Mode.FLEX_FREE- Any other value (e.g.
"GSN-HydroCel-185.csv") setsMontage.Mode.NET
Parameters¶
montage_names : list[str]
Names to look up in the montage file.
eeg_net : str
EEG net identifier that selects the sub-dict inside
montage_list.json["nets"].
include_flex : bool, optional
If True (default), append flex/freehand montages loaded
from the FLEX_MONTAGES_FILE environment variable.
Returns¶
list[Montage] Resolved montage objects ready for simulation.
See Also¶
list_montage_names : Discover available names before loading. upsert_montage : Add montages that can then be loaded. Montage : The returned dataclass type.
Source code in tit/sim/utils.py
tit.sim.utils.list_montage_names ¶
List all montage names defined under an EEG net.
Parameters¶
eeg_net : str
EEG net identifier (e.g. "GSN-HydroCel-185.csv").
mode : str
"U" for uni-polar montage names or "M" for
multi-polar montage names.
Returns¶
list[str] Sorted montage names. Returns an empty list if the net or mode key does not exist.
See Also¶
upsert_montage : Add montage names to the list.
load_montages : Load the named montages as Montage objects.
Source code in tit/sim/utils.py
tit.sim.utils.load_montage_data ¶
load_montage_data() -> dict
Load the full montage_list.json as a dict.
Returns¶
dict
Parsed JSON with top-level key "nets" mapping EEG net
names to their uni/multi polar montage definitions.
See Also¶
save_montage_data : Write the dict back to disk. ensure_montage_file : Guarantees the file exists before reading.
Source code in tit/sim/utils.py
tit.sim.utils.save_montage_data ¶
save_montage_data(data: dict) -> None
Write data to montage_list.json, overwriting the file.
Parameters¶
data : dict
Full montage dict (must contain a "nets" key).
See Also¶
load_montage_data : Read the data back after saving.
Source code in tit/sim/utils.py
tit.sim.utils.ensure_montage_file ¶
ensure_montage_file() -> str
Return the path to montage_list.json, creating it if absent.
If the file does not exist, creates it with the default schema
{"nets": {}}.
Returns¶
str
Absolute path to the montage_list.json file.
See Also¶
load_montage_data : Read the file returned by this function. save_montage_data : Write data to the file returned by this function.
Source code in tit/sim/utils.py
tit.sim.utils.upsert_montage ¶
upsert_montage(*, eeg_net: str, montage_name: str, electrode_pairs: list[list[str]], mode: str) -> None
Insert or update a montage definition in montage_list.json.
Creates the EEG net entry if it does not already exist.
Parameters¶
eeg_net : str
EEG net identifier (e.g. "GSN-HydroCel-185.csv").
montage_name : str
Human-readable montage name.
electrode_pairs : list[list[str]]
List of electrode pairs, each a two-element list of electrode
labels (e.g. [["E1", "E2"], ["E3", "E4"]]).
mode : str
"U" for uni-polar montages (2-pair TI) or "M" for
multi-polar montages (4-pair mTI).
See Also¶
list_montage_names : List montage names after upserting.
load_montages : Load upserted montages as Montage objects.