utils
tit.sim.utils ¶
Shared utilities for TI/mTI simulations.
This module provides all non-class helpers consumed by the simulation package:
- Montage file I/O -- CRUD operations on
montage_list.json. - Montage loading -- resolve EEG-cap and flex/freehand montages.
- Directory setup -- create the BIDS output directory tree.
- Montage visualisation -- render 2-D montage diagrams.
- Post-processing helpers -- field extraction, NIfTI conversion, T1-to-MNI transform, file moves.
- Simulation orchestration -- sequential montage execution.
Public API¶
run_simulation
Execute simulations for every montage in a configuration.
load_montages
Load named montages from montage_list.json.
list_montage_names
List montage names defined under an EEG net.
load_montage_data
Load the full montage_list.json as a dict.
save_montage_data
Write a montage dict to montage_list.json.
ensure_montage_file
Return (and optionally create) the montage_list.json path.
upsert_montage
Insert or update a montage definition.
See Also¶
tit.sim.config : Dataclasses consumed by the functions here. tit.sim.base : Base class that calls directory-setup and viz helpers. tit.sim.TI : 2-pair TI post-processing that uses extract/transform helpers. tit.sim.mTI : N-pair mTI post-processing that uses extract/transform helpers.
ensure_montage_file ¶
ensure_montage_file(*, pm: PathManager | None = None) -> 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
load_montage_data ¶
load_montage_data(*, pm: PathManager | None = None) -> 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
save_montage_data ¶
save_montage_data(data: dict, *, pm: PathManager | None = None) -> 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
ensure_eeg_net_entry ¶
ensure_eeg_net_entry(eeg_net: str) -> None
Ensure an entry for eeg_net exists in montage_list.json.
If the net is not yet present, creates it with empty
uni_polar_montages and multi_polar_montages dicts.
Parameters¶
eeg_net : str
EEG net identifier (e.g. "GSN-HydroCel-185.csv").
See Also¶
upsert_montage : Add a specific montage under an EEG net.
Source code in tit/sim/utils.py
upsert_montage ¶
upsert_montage(*, eeg_net: str, montage_name: str, electrode_pairs: list[list[str]], mode: str, pm: PathManager | None = None) -> 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 CSV filename exactly as it appears in the subject's
eeg_positions folder, e.g. "GSN-HydroCel-185.csv" or
"EEG10-10_UI_Jurak_2007.csv". Created in the file if absent.
montage_name : str
Montage name; an existing montage of the same name under this net
and mode is replaced.
electrode_pairs : list[list[str]]
Electrode pairs, each a two-element list of labels from that net
(e.g. [["E010", "E011"], ["E012", "E013"]]). Two pairs for
mode="U", four (or more, even) for mode="M".
mode : str
"U" for uni-polar montages (2-pair TI) or "M" for
multi-polar montages (4+-pair mTI). Case-insensitive; anything
other than "U" is treated as "M".
pm : PathManager or None, optional
Path manager to locate the project; None uses the global one.
Returns¶
None The file is rewritten in place.
Examples¶
from tit.sim import upsert_montage, list_montage_names upsert_montage( ... eeg_net="GSN-HydroCel-185.csv", ... montage_name="L_Insula", ... electrode_pairs=[["E010", "E011"], ["E012", "E013"]], ... mode="U", ... ) # doctest: +SKIP list_montage_names("GSN-HydroCel-185.csv", mode="U") # doctest: +SKIP ['L_Insula']
See Also¶
list_montage_names : List montage names after upserting.
load_montages : Load upserted montages as Montage objects.
Source code in tit/sim/utils.py
list_montage_names ¶
List all montage names defined under an EEG net.
These are exactly the names :func:load_montages can resolve for
that net; a name not in this list is silently skipped by
:func:load_montages.
Parameters¶
eeg_net : str
EEG-net CSV filename (e.g. "GSN-HydroCel-185.csv",
"EEG10-10_UI_Jurak_2007.csv"), the key under
montage_list.json["nets"].
mode : str
"U" for uni-polar (2-pair TI) montage names or "M" for
multi-polar (4+-pair mTI) montage names. Case-insensitive.
Returns¶
list[str] Sorted montage names. Returns an empty list if the net or mode key does not exist (never raises for an unknown net).
Examples¶
from tit.sim import list_montage_names list_montage_names("GSN-HydroCel-185.csv", mode="U") # doctest: +SKIP ['L_Insula', 'R_Insula'] list_montage_names("GSN-HydroCel-185.csv", mode="M") # doctest: +SKIP []
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
load_flex_montages ¶
Load flex/freehand montage definitions from a JSON file.
Parameters¶
flex_file : str or None, optional
Path to the flex montages JSON file. Falls back to the
FLEX_MONTAGES_FILE environment variable if not provided.
Returns¶
list[dict] List of raw flex montage dicts. Returns an empty list if no file is found.
See Also¶
parse_flex_montage : Convert each returned dict into a Montage.
load_montages : Calls this function when include_flex=True.
Source code in tit/sim/utils.py
parse_flex_montage ¶
Convert a raw flex montage dict into a Montage dataclass.
Parameters¶
flex : dict
Dict with keys "name", "type", and either "pairs"
(for flex_mapped) or "electrode_positions" (for
flex_optimized / freehand_xyz).
Returns¶
Montage
Populated Montage instance.
Raises¶
ValueError
If flex["type"] is not a recognised montage type.
See Also¶
load_flex_montages : Produces the dicts consumed by this function. Montage : The returned dataclass.
Source code in tit/sim/utils.py
load_montages ¶
Load the named montages from the project's montage_list.json.
Only the montages named in montage_names are returned, in that
order -- this is not a listing of every montage under the net.
load_montages(["L_Insula"], ...) returns a one-element list, so
indexing [1] raises IndexError. Use
:func:list_montage_names to see which names exist. A name that is
not defined under eeg_net is silently skipped (no error), so the
result can be shorter than montage_names; check len() or the
returned .name values before relying on positions.
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 and the
FLEX_MONTAGES_FILE environment variable points at a file, any
flex/freehand montages found via :func:load_flex_montages are
appended after the named ones (nothing is appended when the variable
is unset, the normal case for scripts).
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, e.g. ["L_Insula"].
Order is preserved in the result.
eeg_net : str
EEG-net CSV filename (e.g. "GSN-HydroCel-185.csv",
"EEG10-10_UI_Jurak_2007.csv") that selects the sub-dict
inside montage_list.json["nets"]; it is also stored on each
returned :class:Montage as eeg_net.
include_flex : bool, optional
If True (default), append flex/freehand montages loaded
from the FLEX_MONTAGES_FILE environment variable.
Returns¶
list[Montage]
One :class:Montage per found name, in montage_names order,
ready to pass as SimulationConfig(montages=...).
Examples¶
from tit.sim import list_montage_names, load_montages list_montage_names("GSN-HydroCel-185.csv", mode="U") # doctest: +SKIP ['L_Insula', 'R_Insula'] montages = load_montages(["L_Insula"], eeg_net="GSN-HydroCel-185.csv") # doctest: +SKIP len(montages), montages[0].name # doctest: +SKIP (1, 'L_Insula') both = load_montages(["L_Insula", "R_Insula"], eeg_net="GSN-HydroCel-185.csv") # doctest: +SKIP [m.name for m in both] # doctest: +SKIP ['L_Insula', 'R_Insula']
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
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 | |
setup_montage_directories ¶
setup_montage_directories(montage_dir: str, mode: SimulationMode) -> dict[str, str]
Create the BIDS-compliant output directory tree for one montage.
Creates sub-directories for high-frequency fields, TI fields,
meshes, NIfTIs, surface overlays, montage images, and
documentation. For mTI mode, additional mTI/ sub-directories
are created.
Parameters¶
montage_dir : str
Root output directory for this montage.
mode : SimulationMode
SimulationMode.TI or SimulationMode.MTI.
Returns¶
dict[str, str]
Mapping of logical names (e.g. "ti_mesh", "hf_niftis")
to their absolute paths.
See Also¶
SimulationMode : Enum controlling which directories are created. BaseSimulation.run : Calls this at the start of each montage pipeline.
Source code in tit/sim/utils.py
run_montage_visualization ¶
run_montage_visualization(montage_name: str, simulation_mode: SimulationMode, eeg_net: str, output_dir: str, logger, electrode_pairs: list | None = None) -> None
Render a 2-D montage diagram for an EEG-cap montage.
Skips rendering for "freehand" and "flex_mode" montages
(no cap layout available).
Parameters¶
montage_name : str
Name of the montage to visualize.
simulation_mode : SimulationMode
SimulationMode.TI or SimulationMode.MTI.
eeg_net : str
EEG net identifier. Visualization is skipped when this is
"freehand" or "flex_mode".
output_dir : str
Directory where the image file is saved.
logger : logging.Logger
Logger instance for status messages.
electrode_pairs : list or None, optional
Electrode pair list to annotate on the diagram. Defaults to
an empty list.
See Also¶
tit.tools.montage_visualizer.visualize_montage : Underlying rendering function.
Source code in tit/sim/utils.py
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 | |
resolve_montage_electrode_coordinates ¶
resolve_montage_electrode_coordinates(config: SimulationConfig, montage: Montage, logger=None) -> tuple[list[list[float]] | None, str | None]
Resolve every electrode in montage to XYZ coordinates when possible.
Source code in tit/sim/utils.py
create_simulation_config_file ¶
create_simulation_config_file(config: SimulationConfig, montage: Montage, documentation_dir: str, logger) -> None
Write a JSON snapshot of the simulation configuration to disk.
Serialises subject ID, montage details, electrode geometry, mapping
options, and a timestamp into config.json inside
documentation_dir.
Parameters¶
config : SimulationConfig
The full simulation configuration.
montage : Montage
The specific montage being simulated.
documentation_dir : str
Directory to write config.json into.
logger : logging.Logger
Logger instance for status messages.
See Also¶
SimulationConfig : The serialised configuration type.
Source code in tit/sim/utils.py
extract_fields ¶
extract_fields(input_mesh: str, output_dir: str, base_name: str, m2m_dir: str, subject_id: str, logger) -> None
Extract grey-matter and white-matter meshes from a full-head mesh.
Crops the input mesh by SimNIBS tissue tags (tag 2 = GM, tag 1 = WM)
and writes the results as separate .msh files.
Parameters¶
input_mesh : str
Path to the full-head .msh file.
output_dir : str
Directory to write the cropped meshes into.
base_name : str
Stem used for output filenames (e.g.
"grey_{base_name}.msh").
m2m_dir : str
Path to the subject's m2m directory (unused but kept for
interface consistency).
subject_id : str
Subject identifier (unused but kept for interface consistency).
logger : logging.Logger
Logger instance for status messages.
See Also¶
transform_to_nifti : Convert the extracted meshes to NIfTI.
Source code in tit/sim/utils.py
transform_to_nifti ¶
transform_to_nifti(mesh_dir: str, output_dir: str, subject_id: str, m2m_dir: str, logger, fields: list[str] | None = None, skip_patterns: list[str] | None = None) -> None
Convert mesh files in a directory to NIfTI volumes.
Delegates to tit.tools.mesh2nii.convert_mesh_dir which
transforms each .msh file into subject-space (and optionally
MNI-space) NIfTI images.
Parameters¶
mesh_dir : str
Directory containing .msh files to convert.
output_dir : str
Directory to write the resulting NIfTI files.
subject_id : str
Subject identifier (unused but kept for interface consistency).
m2m_dir : str
Path to the subject's m2m directory, used for coordinate
transforms.
logger : logging.Logger
Logger instance for status messages.
fields : list[str] or None, optional
Mesh field names to convert. Converts all fields if None.
skip_patterns : list[str] or None, optional
Filename patterns to skip during conversion.
See Also¶
extract_fields : Produces meshes consumed by this function. convert_t1_to_mni : Companion T1-to-MNI transform.
Source code in tit/sim/utils.py
transform_dirs_to_nifti ¶
transform_dirs_to_nifti(specs: list[dict], m2m_dir: str, logger, *, map_to_mni: bool = False) -> None
Convert several mesh directories to NIfTI in a single process pool.
Thin wrapper over tit.tools.mesh2nii.convert_mesh_dirs. Overlaps
directories that would otherwise be converted one after another (e.g.
the TI-mesh and HF-mesh directories) by submitting every conversion
task to one shared pool.
Parameters¶
specs : list[dict]
One dict per directory with keys mesh_dir and output_dir
(required) and optional fields / skip_patterns.
m2m_dir : str
Path to the subject's m2m directory, used for coordinate transforms.
logger : logging.Logger
Logger instance for status messages.
See Also¶
transform_to_nifti : Convert a single directory.
Source code in tit/sim/utils.py
start_t1_to_mni ¶
Launch the subject T1-to-MNI warp and return the running process.
The subject2mni transform is independent of the simulation field
meshes, so it can run concurrently with the mesh-to-NIfTI conversions.
Pair this with :func:finish_t1_to_mni to wait for completion.
Parameters¶
m2m_dir : str
Path to the subject's m2m directory containing T1.nii.gz.
subject_id : str
Subject identifier, used for the output filename.
Returns¶
subprocess.Popen
The running subject2mni process.
Source code in tit/sim/utils.py
finish_t1_to_mni ¶
Wait for a :func:start_t1_to_mni process and log any failure.
Logs a warning (but does not raise) if the conversion fails or times out, matching the previous best-effort behaviour.
Parameters¶
proc : subprocess.Popen
Process returned by :func:start_t1_to_mni.
logger : logging.Logger
Logger instance for warning messages.
timeout : int
Seconds to wait before killing the process.
Source code in tit/sim/utils.py
convert_t1_to_mni ¶
Convert the subject's T1 image to MNI space via subject2mni.
Blocking convenience wrapper around :func:start_t1_to_mni /
:func:finish_t1_to_mni. Logs a warning (but does not raise) if the
conversion fails.
Parameters¶
m2m_dir : str
Path to the subject's m2m directory containing T1.nii.gz.
subject_id : str
Subject identifier, used for the output filename.
logger : logging.Logger
Logger instance for status/warning messages.
See Also¶
transform_to_nifti : Companion mesh-to-NIfTI transform.
Source code in tit/sim/utils.py
safe_move ¶
Move a file or directory from src to dest.
Ignore a missing AppleDouble sidecar only after its data file has moved.
Parameters¶
src : str Source path. dest : str Destination path.
See Also¶
shutil.move : Underlying implementation.
Source code in tit/sim/utils.py
build_simulation_config_for_job ¶
build_simulation_config_for_job(subject_id: str, montage: Montage, current_str: str, conductivity: str, electrode_shape: str, electrode_dimensions: list[float], gel_thickness: float, output_fields: list[str] | None = None) -> SimulationConfig
Build the backend config for one subject/montage job.
Source code in tit/sim/utils.py
run_simulation ¶
run_simulation(config: SimulationConfig, logger: Logger | None = None, progress_callback: Callable[[int, int, str], None] | None = None, *, overwrite: bool = False) -> 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_avgplusTI_normal(2-pair TI only), or the multi-channel superposition for mTI. - 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.
bool, optional
Explicitly allow SimNIBS to rerun in the montage output directory. Defaults to False; existing-result protection stays enabled unless confirmed.
Returns¶
list[dict]
One result dict per montage with keys montage_name,
montage_type, status, and output_mesh.
Raises¶
ValueError
If config.montages is empty, the subject's m2m_<id>
directory does not exist, a montage has an invalid pair count
(must be an even number >= 2), too few intensities are given
for a montage, a label-based montage has no eeg_net or its
EEG-net CSV is missing from the subject's eeg_positions
folder, or an XYZ montage holds a malformed coordinate.
OSError
Raised by SimNIBS when a montage output directory already holds
results and overwrite is False.
Examples¶
from tit.sim import SimulationConfig, load_montages, run_simulation montages = load_montages(["L_Insula"], eeg_net="GSN-HydroCel-185.csv") # doctest: +SKIP cfg = SimulationConfig(subject_id="ernie", montages=montages, ... intensities=[1.0, 1.0], output_fields=["TI_max"]) # doctest: +SKIP results = run_simulation(cfg) # doctest: +SKIP results[0]["status"], results[0]["output_mesh"] # doctest: +SKIP ('completed', '.../Simulations/L_Insula/TI/mesh/ernie_L_Insula_TI.msh')
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
960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 | |