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 -- build
Montageobjects frommontage_list.json. - 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
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 is
replaced, whichever polarity it was saved under (one name, one
Simulations/<name>/ folder).
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_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. A name saved
under both the uni- and the multi-polar section raises ValueError.
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.
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
Ignored. It once appended flex montages named by an environment
variable that nothing sets any more; it is still accepted so
scripts and notebooks that pass it keep running.
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
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 | |
setup_montage_directories ¶
setup_montage_directories(montage_dir: str, mode: SimulationMode, *, niftis: bool = False) -> dict[str, str]
Create the BIDS-compliant output directory tree for one montage.
Creates sub-directories for the high-frequency (channel) fields, the
run's field mesh and its central surface, montage images and
documentation: under TI/ for a TI run and mTI/ for an mTI run.
Parameters¶
montage_dir : str
Root output directory for this montage.
mode : SimulationMode
SimulationMode.TI or SimulationMode.MTI.
niftis : bool
Also create the niftis/ folders (the run writes NIfTIs, map_to_vol). Their
paths are always in the mapping; a step that writes one later creates the folder.
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
existing_result_meshes ¶
Result meshes an earlier run left in montage_dir (TI/mesh or mTI/mesh).
Parameters¶
montage_dir : str Root output directory of one montage.
Returns¶
list[str]
Sorted paths of the .msh files found; empty when there is no result.
Source code in tit/sim/utils.py
refuse_existing_result ¶
Refuse to run a montage whose folder already holds an earlier result.
SimNIBS's own guard looks for its session record in high_Frequency/,
which _organize_files moves into documentation/, so after a
completed run it never fires: the result mesh is the toolbox's marker.
Parameters¶
montage_dir : str Root output directory of one montage. montage_name : str The montage's name, for the message.
Raises¶
FileExistsError
If :func:existing_result_meshes finds a mesh in montage_dir.
Source code in tit/sim/utils.py
clear_previous_outputs ¶
clear_previous_outputs(montage_dir: str) -> None
Remove the derived outputs of earlier runs before a confirmed re-run.
Removes TI/, mTI/ and fsaverage/ and everything in
high_Frequency/ except SimNIBS session records, so the folder ends up
holding exactly what the new config.json asks for. The run history
(documentation/, session records) and the user's Analyses/ are
kept, and so is anything else the simulator does not write. A folder that
is a symbolic link is left as it is, link and target: what it points at is
not this montage's to delete.
Parameters¶
montage_dir : str Root output directory of one montage.
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
486 487 488 489 490 491 492 493 494 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 | |
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. It records what this montage runs with:
intensities holds one current per pair (SimNIBS reads the first two
for TI and one per pair for mTI, though a config may list more), and
tissue_conductivities the per-tissue overrides (S/m, keyed by tissue
number) that BaseSimulation._apply_tissue_conductivities applies from
SimulationConfig.tissue_conductivities.
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
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 / tissues.
m2m_dir : str
Path to the subject's m2m directory, used for coordinate transforms.
logger : logging.Logger
Logger instance for status messages.
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
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
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 the field mesh, its central surface and (opt-in) 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
Replace an earlier result of a montage. Defaults to False; existing-result
protection stays enabled unless confirmed. When set, the earlier run's
derived outputs (TI/, mTI/, fsaverage/, high_Frequency/) are
removed before solving, keeping documentation/ and Analyses/, so the
folder holds exactly what the new config.json says.
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
FileExistsError when a montage folder already holds a result mesh
(TI/mesh or mTI/mesh) and overwrite is False; every
montage is checked before the first is solved, so nothing is
written. SimNIBS raises its own OSError when an interrupted run
left a session record in high_Frequency/.
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
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 | |