Skip to content

utils

tit.opt.flex.utils

ROI configuration and output naming for flex-search.

Provides helper functions for directory naming, human-readable labelling, SimNIBS log parsing, and ROI setup on TesFlexOptimization objects.

Public API

generate_run_dirname Create a datetime-stamped directory name for a run. generate_label Build a human-readable label string for a flex-search run. parse_optimization_output Extract the goal-function value from a SimNIBS log line. configure_roi Set up ROI(s) on a SimNIBS optimization object.

See Also

tit.opt.flex.flex : Main flex-search orchestrator.

generate_run_dirname

generate_run_dirname(base_path: str) -> str

Generate a datetime-stamped directory name for a flex-search run.

Format is YYYYMMDD_HHMMSS. When a collision exists, _1, _2, etc. are appended.

Parameters

base_path : str Parent directory (e.g. flex-search/) to check for collisions.

Returns

str Directory name string (not the full path).

Source code in tit/opt/flex/utils.py
def generate_run_dirname(base_path: str) -> str:
    """Generate a datetime-stamped directory name for a flex-search run.

    Format is ``YYYYMMDD_HHMMSS``.  When a collision exists, ``_1``,
    ``_2``, etc. are appended.

    Parameters
    ----------
    base_path : str
        Parent directory (e.g. ``flex-search/``) to check for
        collisions.

    Returns
    -------
    str
        Directory name string (not the full path).
    """
    from datetime import datetime

    name = datetime.now().strftime("%Y%m%d_%H%M%S")
    if not os.path.exists(os.path.join(base_path, name)):
        return name
    suffix = 1
    while os.path.exists(os.path.join(base_path, f"{name}_{suffix}")):
        suffix += 1
    return f"{name}_{suffix}"

generate_label

generate_label(config, pareto: bool = False) -> str

Build a human-readable label for a flex-search run.

The label is stored in flex_meta.json for GUI display and is not used for folder naming or machine parsing.

Parameters

config : FlexConfig Flex-search configuration instance. pareto : bool, optional When True the goal component is replaced by "pareto".

Returns

str Label string, e.g. "mean_maxTI_sphere(-42,-20,55)r10".

Source code in tit/opt/flex/utils.py
def generate_label(config, pareto: bool = False) -> str:
    """Build a human-readable label for a flex-search run.

    The label is stored in ``flex_meta.json`` for GUI display and is
    **not** used for folder naming or machine parsing.

    Parameters
    ----------
    config : FlexConfig
        Flex-search configuration instance.
    pareto : bool, optional
        When *True* the goal component is replaced by ``"pareto"``.

    Returns
    -------
    str
        Label string, e.g. ``"mean_maxTI_sphere(-42,-20,55)r10"``.
    """
    postproc_short = {
        "max_TI": "maxTI",
        "dir_TI_normal": "normalTI",
        "dir_TI_tangential": "tangentialTI",
    }
    pp = postproc_short.get(
        (
            config.postproc.value
            if hasattr(config.postproc, "value")
            else str(config.postproc)
        ),
        str(config.postproc),
    )

    goal_str = (
        "pareto"
        if pareto
        else (config.goal.value if hasattr(config.goal, "value") else str(config.goal))
    )

    roi = config.roi
    if isinstance(roi, FlexConfig.SphericalROI):
        x = int(roi.x) if roi.x == int(roi.x) else roi.x
        y = int(roi.y) if roi.y == int(roi.y) else roi.y
        z = int(roi.z) if roi.z == int(roi.z) else roi.z
        r = int(roi.radius) if roi.radius == int(roi.radius) else roi.radius
        roi_str = f"sphere({x},{y},{z})r{r}"
    elif isinstance(roi, FlexConfig.AtlasROI):
        hemi = roi.hemisphere
        atlas = (
            os.path.basename(roi.atlas_path).replace(".annot", "").split(".")[-1]
            if roi.atlas_path
            else "atlas"
        )
        roi_str = f"{hemi}-{atlas}-{roi.label}"
    elif isinstance(roi, FlexConfig.SubcorticalROI):
        atlas = os.path.basename(roi.atlas_path) if roi.atlas_path else "volume"
        for ext in (".nii.gz", ".nii", ".mgz"):
            if atlas.endswith(ext):
                atlas = atlas[: -len(ext)]
                break
        roi_str = f"subcortical-{atlas}-{roi.label}"
    else:
        roi_str = "unknown"

    return f"{goal_str}_{pp}_{roi_str}"

parse_optimization_output

parse_optimization_output(line: str) -> float | None

Extract the goal-function value from a SimNIBS log line.

Recognises three patterns:

  • "Final goal function value: -42.123"
  • "Goal function value<anything>: -42.123"
  • Table row with a max_TI column (optionally in scientific notation)
Parameters

line : str A single line of SimNIBS stdout / stderr.

Returns

float or None The extracted function value, or None when the line does not match any known pattern.

Source code in tit/opt/flex/utils.py
def parse_optimization_output(line: str) -> float | None:
    """Extract the goal-function value from a SimNIBS log line.

    Recognises three patterns:

    * ``"Final goal function value:   -42.123"``
    * ``"Goal function value<anything>:  -42.123"``
    * Table row with a ``max_TI`` column (optionally in scientific
      notation)

    Parameters
    ----------
    line : str
        A single line of SimNIBS stdout / stderr.

    Returns
    -------
    float or None
        The extracted function value, or *None* when the line does not
        match any known pattern.
    """
    import re

    # Primary: "Final goal function value: <number>"
    m = re.search(
        r"Final goal function value:\s*([+-]?[\d.eE+-]+)", line, re.IGNORECASE
    )
    if m:
        return float(m.group(1))

    # Secondary: "Goal function value<anything>: <number>"
    m = re.search(r"Goal function value[^:]*:\s*([+-]?[\d.eE+-]+)", line, re.IGNORECASE)
    if m:
        return float(m.group(1))

    # Table row: "|max_TI | 0.025" or "max_TI | 0.025e-03"
    m = re.search(r"\|?\s*max_TI\s+\|\s*([\d.+-]+)(?:e([+-]?\d+))?", line)
    if m:
        base = float(m.group(1))
        exp = int(m.group(2)) if m.group(2) else 0
        val = base * (10**exp)
        if val > 0:
            return val

    return None

configure_roi

configure_roi(opt, config: FlexConfig) -> None

Configure ROI(s) on a SimNIBS optimization object.

Delegates to the appropriate private helper based on the ROI type stored in config.roi (spherical, atlas, or subcortical).

Parameters

opt : simnibs.optimization.TesFlexOptimization SimNIBS optimization object to configure. config : FlexConfig Flex-search configuration containing the ROI specification.

Raises

ValueError If the ROI type is not recognised.

Source code in tit/opt/flex/utils.py
def configure_roi(opt, config: FlexConfig) -> None:
    """Configure ROI(s) on a SimNIBS optimization object.

    Delegates to the appropriate private helper based on the ROI type
    stored in ``config.roi`` (spherical, atlas, or subcortical).

    Parameters
    ----------
    opt : simnibs.optimization.TesFlexOptimization
        SimNIBS optimization object to configure.
    config : FlexConfig
        Flex-search configuration containing the ROI specification.

    Raises
    ------
    ValueError
        If the ROI type is not recognised.
    """
    if isinstance(config.roi, FlexConfig.SphericalROI):
        _configure_spherical_roi(opt, config)
    elif isinstance(config.roi, FlexConfig.AtlasROI):
        _configure_atlas_roi(opt, config)
    elif isinstance(config.roi, FlexConfig.SubcorticalROI):
        _configure_subcortical_roi(opt, config)
    else:
        raise ValueError(f"Unknown ROI type: {type(config.roi)}")