volume_fields
tit.volume_fields ¶
A simulation's volume fields: computed by the simulator, produced again on demand.
:func:compute_volume_fields is the one per-field computation. The simulator writes its output
into the montage's volume mesh (:meth:tit.sim.base.BaseSimulation._write_volume_mesh), and the
on-demand path below produces a field the simulation did not save from the same inputs: the
channel meshes every run keeps in high_Frequency/mesh/ (<sid>_TDCS_<k>_<cond>.msh, k
= 1, 2 for TI and A..Z for mTI), cropped to brain tissue as the simulator crops them.
What persists, so the next step finds the field saved:
- :func:
ensure_mesh_fieldsadds the field to the volume mesh<sim>/<TI|mTI>/mesh/<sim>_<TI|mTI>.mshand rewrites its central surface<sim>/<TI|mTI>/mesh/surfaces/<sim>_<TI|mTI>_central.msh(:func:central_surface: every volume field andTI_normalas node data), which is what a mesh analysis reads. It also builds that surface for a run that lacks it or whose surface lacks a field -- a run made before the surface carriedTI_normal-- takingTI_normalfrom the run's old<sim>_normal.msh(<sim>_mTI_normal.msh) when it has one, else from the channel meshes. It never deletes an old run's files; nothing reads them; - :func:
ensure_niftis/ :func:ensure_nifti_filealso write the NIfTI a voxel step reads, under the simulator's own name (<tissue_><sim>_<TI|mTI>_subject_<field>.nii.gz, or..._MNI_MNI_<field>.nii.gzin MNI space) in<sim>/<TI|mTI>/niftis/; agrey_orwhite_NIfTI is converted from the volume mesh cropped to that tissue (:data:tit.tools.mesh2nii.TISSUE_TAGS).
NIfTIs and the mesh stay consistent. A run that wrote NIfTIs (map_to_vol in its run record)
has one per mesh field, so a field added to its mesh gets its NIfTIs too -- every converted
mesh, subject space and, with map_to_mni, MNI space -- and the set still equals the mesh field
set. A run with NIfTIs off has no such set: its niftis/ holds only what voxel steps asked for,
each a NIfTI of a field its mesh carries, never one the mesh lacks.
Writes are crash-safe: everything is staged in a hidden folder and moved into place with atomic
renames, derived files first and the volume mesh last (:func:_produce_mesh_fields).
Each says one plain sentence per production before doing it ("hf_peak and hf_rms were not saved
for L_Insula; computing them from the channel fields and saving them to L_Insula_TI.msh."),
through the caller's say (its own logger.info, so it reaches that job's output), and
returns the sentences so the caller can show them wherever its results are read.
Nothing is guessed: a field that cannot be produced (no channel meshes, no recorded config, an
envelope of a carrier-only run, hf_power_density whose conductivity is not kept) raises
:class:FieldUnavailableError naming what is missing.
FieldUnavailableError ¶
Bases: FileNotFoundError
A field a step needs was not saved and cannot be produced from what the simulation kept.
hf_power_density ¶
Absorbed (Joule) power density of the carriers, time-averaged, W/m^3.
(1/2) sum_c E_c^T sigma E_c for zero-to-peak carrier fields E_c
(V/m) at distinct frequencies: sigma * hf_power for isotropic
sigma (N,) S/m, the full tensor form for (N, 9) anisotropic
conductivities (J . E averaged over time). Not SAR (that is this over the
tissue density, IEEE C95.1-2019 p. 35). coherent is the carrier-only control: one
sinusoid of amplitude sum_c E_c.
Source code in tit/volume_fields.py
compute_volume_fields ¶
compute_volume_fields(e_fields: list, names, *, max_name: str, coherent: bool, sigma=None) -> list[tuple[str, ndarray]]
(on-disk name, values) of each selected volume field, over all channel fields jointly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
e_fields
|
list
|
The channels' |
required |
names
|
Logical field names (:data: |
required | |
max_name
|
str
|
The envelope maximum's on-disk name ( |
required |
coherent
|
bool
|
Carrier-only control: the channel fields add coherently. |
required |
sigma
|
Per-element conductivity of the solve, for |
None
|
Source code in tit/volume_fields.py
central_surface ¶
The subject's central cortical surface (lh then rh) carrying volume's fields as node data.
What SimNIBS's msh2cortex does, in process: the brain-tissue volume mesh is cropped to
white matter, grey matter and CSF, and its fields are interpolated onto the m2m central
surfaces inside the grey-matter tetrahedra (ElementData.interpolate_to_surface), all of
them in one stacked call per hemisphere; the hemispheres are joined as msh2cortex joins
them (tags LH_/RH_CENTRAL_SURFACE). With e_fields -- the channels' E on
volume's elements, in channel order -- their interpolated vectors give TI_normal, the
envelope along the node normals (:func:tit.calc.envelope), as the old surface overlays'
E_normal gave it (tests/numerical/test_central_surface_real.py: both to 3e-14 V/m).
Raises:
| Type | Description |
|---|---|
ValueError
|
volume carries a field that is not a scalar. |
Source code in tit/volume_fields.py
check_surface_producible ¶
Raise :class:FieldUnavailableError if the central surface is incomplete and cannot be built.
Filesystem checks only (headers and file existence), for a job's preflight.
Source code in tit/volume_fields.py
check_producible ¶
Raise :class:FieldUnavailableError if a field of fields is neither saved nor producible.
Filesystem checks only (no mesh is loaded), for a job's preflight.
Source code in tit/volume_fields.py
ensure_mesh_fields ¶
ensure_mesh_fields(subject_id: str, simulation: str, fields, *, say: Callable[[str], None] = _quiet, surface: bool = True) -> list[str]
Make the volume mesh carry every field of fields, and its central surface all of them.
A missing field is computed with :func:compute_volume_fields from the channel meshes and
added to the volume mesh, and the central surface is rewritten from it with every field.
A central surface that is missing or lacks a field of the volume mesh or TI_normal is
rebuilt even when fields is empty (an old run, :func:_produce_surface). Returns one
sentence per production (empty when nothing was needed), each handed to say before the
work it announces starts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
subject_id
|
str
|
Subject identifier (no |
required |
simulation
|
str
|
Simulation (montage) folder name. |
required |
fields
|
Logical or on-disk volume field names ( |
required | |
say
|
Callable[[str], None]
|
Called with each sentence first (a caller's |
_quiet
|
surface
|
bool
|
Also rebuild an incomplete central surface when no field is missing.
|
True
|
Raises:
| Type | Description |
|---|---|
FieldUnavailableError
|
a missing field or the surface cannot be produced. |
ValueError
|
a name is not a volume field ( |
RuntimeError
|
the head model's central surface is not the one the run used. |
Source code in tit/volume_fields.py
ensure_niftis ¶
ensure_niftis(subject_id: str, simulation: str, fields, *, tissue: str | None, space: str = 'subject', say: Callable[[str], None] = _quiet) -> tuple[dict[str, Path], list[str]]
The NIfTI of each field of fields for one tissue and space, produced if not saved.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
subject_id
|
str
|
Subject identifier (no |
required |
simulation
|
str
|
Simulation (montage) folder name. |
required |
fields
|
Volume field names, as for :func: |
required | |
tissue
|
str | None
|
|
required |
space
|
str
|
|
'subject'
|
say
|
Callable[[str], None]
|
As for :func: |
_quiet
|
Returns:
| Type | Description |
|---|---|
tuple[dict[str, Path], list[str]]
|
|
Raises:
| Type | Description |
|---|---|
FieldUnavailableError
|
a missing field cannot be produced. |
RuntimeError
|
the conversion ran but did not write a requested file. |
Source code in tit/volume_fields.py
voxelise ¶
Subject-space NIfTIs of fields of the field mesh mesh (cropped to tissue).
Written into out_dir as <tissue_><mesh stem>_subject_<field>.nii.gz.
The conversion the on-demand path uses, for a caller that keeps the volumes elsewhere (the simulation report voxelises its envelope into a scratch folder). Keyed by field.
Raises:
| Type | Description |
|---|---|
FieldUnavailableError
|
mesh is missing, or does not carry one of fields. |
RuntimeError
|
the conversion did not write a requested file. |
Source code in tit/volume_fields.py
check_nifti_producible ¶
Whether path is a NIfTI this simulation writes and could produce now (raises if not).
False when the name is not one the simulator writes (the caller's own missing-file
message applies); raises :class:FieldUnavailableError when it is, but its field cannot
be produced (no field mesh, no channel meshes, ...).
Source code in tit/volume_fields.py
ensure_nifti_file ¶
ensure_nifti_file(subject_id: str, simulation: str, path, *, say: Callable[[str], None] = _quiet) -> list[str]
Produce the simulation NIfTI path if it is missing; the sentences said.
path is a name the simulator writes (simulation_nifti_path resolves the group tools'
patterns to one) of a simulation that has its field mesh. Any other missing file -- another
name, a simulation that is not there -- is left to the caller to report as missing.
Raises:
| Type | Description |
|---|---|
FieldUnavailableError
|
the field mesh is there, but the field cannot be produced. |
Source code in tit/volume_fields.py
check_niftis_match_meshes ¶
check_niftis_match_meshes(volume_mesh, niftis_dir, *, mni: bool) -> None
Raise unless volume_mesh has exactly one NIfTI per field, per tissue and space.
The simulator's NIfTI set is its mesh field set, name for name: <base>_subject_<field>
(and <base>_MNI_MNI_<field> with mni) for each field, where <base> is the volume
mesh's stem and its grey_ / white_ crops, and nothing else under those names.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
the mesh's fields cannot be read, or the NIfTIs differ from them. |