Skip to content

PlanTUS placement pipeline – step-by-step

⚠️ Experimental. PlanTUS placement is under active development. Results should be verified manually before use in a study.

This document describes the sequence of operations performed when placement.mode = 'plantus' is set in a PRESTUS configuration.

Overview

Given a T1-weighted image, a SimNIBS head mesh, a target-ROI mask, and a transducer configuration YAML, PlanTUS ranks every skin surface vertex as a candidate transducer position.

Two modes are available, controlled by parameters.simulation.interactive:

Mode Script Vertex selection
Headless (interactive = 0, default) prestus_plantus_launcher.py Automatic — weighted composite score, no display required
Interactive (interactive = 1) prestus_plantus_gui_shim.py Manual — five scoring maps displayed in Connectome Workbench; user clicks a vertex and confirms at the console prompt

Both modes compute the same scoring maps (steps 2–4 below) and write the same output files. Interactive mode additionally requires pynput and Connectome Workbench wb_view; headless mode requires neither.

The original PlanTUS_wrapper.py (Mainz group, available separately at placement.plantus.script_path) is not called directly because it has hard-coded paths and unconditionally imports pynput. The two PRESTUS scripts above replicate the identical computation while accepting CLI arguments.


Step-by-step pipeline

1 Parse inputs

Input Description
t1_filepath Subject T1.nii.gz (SimNIBS output)
simnibs_mesh_filepath Head mesh sub-XXX.msh (CHARM output)
target_mask_filepath Binary NIfTI sphere centred on the target
tx_config_yaml Transducer + placement parameters (written by PRESTUS)

The YAML is a flat key–value file.
Key fields: max_distance, min_distance, transducer_diameter, plane_offset, additional_offset, focal_distance_list, flhm_list, five weight_* scalars.


2 Convert SimNIBS mesh to surfaces

PlanTUS.convert_simnibs_mesh_to_surface extracts triangulated .surf.gii files from the volumetric .msh mesh by tissue tag:

Surface SimNIBS tags File
Skin 1005 skin.surf.gii
Skull 1007, 1008 skull.surf.gii

SimNIBS also produces skull.stl (used in the skull-entry-angle metric below).


3 Avoidance mask (create_avoidance_mask)

Identifies skin vertices that must not be used as transducer positions:

  1. Air cavities / sinuses – binarise final_tissues.nii.gz, fill holes with wb_command -volume-fill-holes, subtract to isolate air; tessellate to STL with FreeSurfer mri_tessellate; any skin vertex whose outward normal ray intersects an air cavity is masked out.
  2. Eyes – extract eye mesh (tag 1006), find left/right centres via k-means on x-coordinates; mask out all skin vertices within 30 mm of each eye centre.
  3. Ears (tragus) – read LPA/RPA from eeg_positions/Fiducials.csv; shift 1.5 cm posterior; mask out all vertices within 15 mm.
  4. Erosion – the binary avoidance metric is eroded by transducer_radius mm to leave a safety margin.

Vertices with avoidance == 0 are excluded from the composite score.

External tools required: fslmaths, wb_command, mri_tessellate, mris_convert. If final_tissues.nii.gz is absent (e.g. CHARM not run) the avoidance step is skipped with a warning and all vertices remain eligible.


4 Per-vertex scoring metrics

For every skin vertex i four metrics are computed:

4a Skin–target distance d_i

Euclidean distance from skin vertex i to the centre of mass of the target mask (from PlanTUS.roi_center_of_gravity).
Lower is better.

4b Skin–target angle α_i

Angle (degrees) between the skin surface normal at i and the vector from i to the target centre of mass.
Lower is better (normal points towards target → good sonication path).

4c Skin–target intersection x_i

The outward-pointing skin normal at i is cast as a ray of length 200 mm into the volume. The ray is intersected with the 3-D surface of the target mask STL (generated by PlanTUS.stl_from_nii). The metric is the total path length of the ray inside the target (chord length for one entry–exit pair, sum of chords for multiple pairs).
Higher is better.

4d Skin–skull angle β_i

The skin normal ray is intersected with the skull STL (ray length 40 mm). The closest skull surface vertex to the intersection point is found; the angle between the skin normal and the skull normal at that vertex is computed.
Lower is better (perpendicular skull entry → lower aberration).

Note: A fifth metric, skull thickness, appears in the BabelBrain weight configuration but is not yet implemented in the open-source PlanTUS release. The corresponding weight (weight_skull_thickness) is accepted in the YAML but the weight is currently absorbed into the other four metrics proportionally.


5a Headless mode — automatic composite scoring

Each metric is normalised to [0, 1] (min–max over all finite values; NaN or infinite values are set to 1 = worst). The intersection metric is inverted (higher intersection → lower normalised cost).

Composite cost for vertex i:

score_i = ( w_dist  × norm(d_i)
          + w_ang   × norm(α_i)
          + w_inter × (1 − norm(x_i))
          + w_skull × norm(β_i) )
        ÷ (w_dist + w_ang + w_inter + w_skull)

Vertices with avoidance == 0 or d_i > max_distance receive score = inf.

The vertex with the minimum composite score is selected as the transducer position.

5b Interactive mode — GUI vertex selection

When parameters.simulation.interactive = 1, a planning scene is built from the four normalised metric maps and loaded into Connectome Workbench (wb_view). The user visually inspects the maps on the inflated skin surface, clicks on a vertex, and at the console prompt types yes to confirm placement (or no to continue browsing). The step repeats until the user closes wb_view.

The interactive mode requires: - pynput Python package (mouse listener for reading wb_view output) - Connectome Workbench wb_view — either on PATH or specified via placement.plantus.connectome_wb_path - A MATLAB desktop session (usejava('desktop') == true)

Multiple vertex confirmations in one session are possible; PRESTUS reads the most recently written *Localite.mat.


6 Placement geometry (prepare_acoustic_simulation)

For the best vertex v:

  1. Determine beam direction: if the skin normal at v intersects the target (x_v > 0), use the negated skin normal; otherwise use the negated skin-to-target vector. Both are unit vectors pointing into the head.

  2. Compute transducer centre:

transducer_centre = skin_coordinate_v
                  − (plane_offset + additional_offset) × beam_direction

plane_offset = distance from radiating surface to transducer exit plane.
additional_offset = gel pad / standoff thickness.

  1. Build a 4 × 4 Localite position matrix (RAS mm) with PlanTUS.create_Localite_position_matrix:
  2. Column 3 (translation) = transducer_centre
  3. Column 0 (x-axis) = beam_direction (unit vector)
  4. Columns 1–2 = orthonormal frame computed via Gram–Schmidt

  5. Save outputs to <mesh_dir>/PlanTUS/<target_name>/vtx<N>/:

File Contents
*_Localite.mat position_matrix (4×4, RAS mm) — used by PRESTUS
*_Localite.txt Plain-text copy
*_kPlan.mat Same matrix in k-Plan convention (m, axes permuted)
*_Localite_XML.txt Fake Localite XML snippet
focus_position_matrix_*.txt Focus point transform
transducer_*.surf.gii Transducer surface model at position

7 PRESTUS reads the output

position_transducer_plantus.m searches for the most recent *Localite.mat under <mesh_dir>/PlanTUS/<target_name>/, loads position_matrix, and passes it to localite_matrix_to_positions to convert the 4×4 RAS matrix into trans_pos and focus_pos in voxel coordinates.

After the placement coordinates are resolved, a T1 overlay plot (plot_placement_t1_overlay) is generated automatically and written to the subject output folder. It shows the final transducer position and target overlaid on orthogonal T1 slices.


Coordinate conventions

Frame Units Description
Voxel / grid integer indices PRESTUS simulation grid
RAS+ (subject) mm T1 image world space; used by Localite matrix
MNI mm Template space; target specified here, mapped to RAS via SimNIBS
k-Plan m, permuted axes Not used by PRESTUS

External dependencies

Tool Headless Interactive Used in step
SimNIBS Python (nibabel, scipy, nilearn) All steps
PlanTUS Python library (PlanTUS.py) All steps
PyYAML (or built-in fallback) YAML config loading
FSL fslmaths Avoidance mask – tissue binarisation
Connectome Workbench wb_command Avoidance mask – hole filling
FreeSurfer mri_tessellate / mris_convert Avoidance mask – air mesh
Connectome Workbench wb_view Interactive scene display
pynput Python package Mouse click listener for wb_view

If the avoidance-mask external tools are unavailable, the launcher proceeds without masking and prints a warning.

Installing pynput for interactive mode

# In the SimNIBS conda environment:
conda activate simnibs_v4.6.0
pip install pynput