Skip to content

flex_search

Flex-search report: one self-contained HTML record per flex-search run.

Built on :mod:tit.reporting.html.components (ARCHITECTURE.md §14). Everything is read from the run folder: flex_meta.json (goal, ROI, currents, electrode, every restart's value), candidate_history/NN/ (the accepted candidate's ROI and background means and currents, and the manifest's metric definitions), electrode_positions.json, the ROI confirmation (roi.tetravox.json), the final per-channel simulations and the run's own figures (valid_skin_region.png, roi.png, embedded as WebP). Rules come from tit.reporting.qc_rules.RULES["opt"].

Flex electrodes sit at free scalp positions, so they are listed by coordinate, not drawn on a cap.

tit.opt.flex writes one when a run finishes. Rebuild one (nothing is re-optimised)::

simnibs_python -m tit.reporting.generators.flex_search <project> <subject> <run folder> [--out DIR]

goal_words

goal_words(meta: dict) -> tuple[str, str, float]

(what the goal maximises, what the score means, the score as a user reads it).

The optimiser minimises, so every score is the negative of the quantity it maximises.

Source code in tit/reporting/generators/flex_search.py
def goal_words(meta: dict) -> tuple[str, str, float]:
    """``(what the goal maximises, what the score means, the score as a user reads it)``.

    The optimiser minimises, so every score is the negative of the quantity it maximises.
    """
    goal, v = meta.get("goal"), -float(meta["result"]["best_value"])
    w = float(meta.get("intensity_weight") or 0.0)
    if goal == "mean":
        return "the mean field in the target", f"mean target field {v:.3f} V/m", v
    if goal == "max":
        return (
            "the peak (99.9th percentile) field in the target",
            f"peak target field {v:.3f} V/m",
            v,
        )
    if goal == "focality":
        return (
            "focality by SimNIBS's threshold-based ROC measure",
            f"ROC focality score {v:.2f} (100 × (√2 − ROC distance); higher is more focal)",
            v,
        )
    power = "" if not w else f"<sup>{1 + w:g}</sup>"
    return (
        "the target's mean field relative to everywhere else",
        f"target mean{power} {v:.2f}× the background mean",
        v,
    )

run_figures

run_figures(run_dir: Path) -> dict[str, bytes]

{file name: WebP bytes} for each of :data:_RUN_FIGURES the run wrote.

Source code in tit/reporting/generators/flex_search.py
def run_figures(run_dir: Path) -> dict[str, bytes]:
    """``{file name: WebP bytes}`` for each of :data:`_RUN_FIGURES` the run wrote."""
    import io

    from PIL import Image

    out = {}
    for name, width in _RUN_FIGURES.items():
        if (run_dir / name).is_file():
            image = Image.open(run_dir / name)
            image.thumbnail((width, width))
            buf = io.BytesIO()
            image.save(buf, "WEBP", quality=80, method=6)
            out[name] = buf.getvalue()
    return out

collect

collect(run_dir: str | Path) -> dict

Everything the report shows, from one flex-search run folder.

Source code in tit/reporting/generators/flex_search.py
def collect(run_dir: str | Path) -> dict:
    """Everything the report shows, from one flex-search run folder."""
    run_dir = Path(run_dir)
    meta = json.loads((run_dir / "flex_meta.json").read_text())
    pos_file = run_dir / "electrode_positions.json"
    positions = json.loads(pos_file.read_text()) if pos_file.is_file() else {}
    carriers = []
    for d in sorted(run_dir.glob("final_sim_*")):
        f = d / "fields_summary.txt"
        if f.is_file():
            carriers += parse_fields_summary(f.read_text())
    logs = sorted(run_dir.glob("simnibs_optimization_*.log"))
    simnibs = None
    if logs:
        m = re.search(r"simnibs (\d[\w.]*)", logs[-1].read_text(errors="replace"))
        simnibs = m.group(1) if m else None
    summary = run_dir / "summary.txt"
    calib = (
        re.search(
            r"calibration error exceeded 10%! Estimated error value: ([\d.]+)%",
            summary.read_text(),
        )
        if summary.is_file()
        else None
    )
    rec = {
        "run": run_dir.name,
        "meta": meta,
        "restarts": _best_rows(run_dir),
        "positions": positions.get("optimized_positions") or [],
        "channel_array": positions.get("channel_array_indices") or [],
        "carriers": carriers,
        "roi": common.roi_summary(run_dir),
        "simnibs": simnibs,
        "calibration_error_pct": float(calib.group(1)) if calib else None,
        "figures": run_figures(run_dir),
    }
    config = (
        rec["restarts"][0]["manifest"].get("config") if rec["restarts"] else None
    ) or {}
    rec["config"] = config
    return rec

currents

currents(rec: dict) -> list[float]

Per-channel currents of the accepted montage (the searched split when there is one).

Source code in tit/reporting/generators/flex_search.py
def currents(rec: dict) -> list[float]:
    """Per-channel currents of the accepted montage (the searched split when there is one)."""
    meta = rec["meta"]
    best = (
        rec["restarts"][meta["result"].get("best_run_index") or 0]["row"]
        if rec["restarts"]
        else {}
    )
    if best.get("current_ch1_mA"):
        return [float(best["current_ch1_mA"]), float(best["current_ch2_mA"])]
    if meta.get("current_split"):
        return [float(v) for v in meta["current_split"]]
    return [float(meta["current_mA"])] * 2

create_flex_search_report

create_flex_search_report(project_dir: str | Path, subject_id: str, run_dir: str | Path, out_dir: str | Path | None = None) -> Path

Write the report for one flex-search run folder; into the project's reports unless out_dir.

Source code in tit/reporting/generators/flex_search.py
def create_flex_search_report(
    project_dir: str | Path,
    subject_id: str,
    run_dir: str | Path,
    out_dir: str | Path | None = None,
) -> Path:
    """Write the report for one flex-search run folder; into the project's reports unless *out_dir*."""
    t0 = time.time()
    rec = collect(run_dir)
    return common.write_report(
        build_html(rec, subject_id),
        project_dir,
        subject_id,
        REPORT_PREFIX,
        SIZE_BUDGET,
        out_dir,
        t0,
        label="Flex-search report",
    )