# BabelBrain -- planning GUI for transcranial focused ultrasound (TUS): takes a # SimNIBS head model, a T1 (and optionally CT/ZTE/PETRA), a trajectory from # Brainsight or 3D Slicer and a transducer model, and produces acoustic and # thermal field predictions with the BabelViscoFDTD solver. Upstream ships # macOS and Windows installers only; this is the Linux package. # # Layout facts the spec depends on (checked against the pinned commit): # - No pyproject.toml, setup.py or dependency metadata upstream; the GUI is # run as `python BabelBrain/BabelBrain.py`. %%prep writes a pyproject.toml. # - Three top-level packages: BabelBrain/ (GUI, Step 1), TranscranialModeling/ # (Step 2) and ThermalModeling/ (Step 3). The last two have no __init__.py # and are imported by name from BabelBrain.py, which puts its parent # directory on sys.path. 0.8.1-6 shipped only BabelBrain/, so Steps 2 and 3 # could not import, and the launcher died on `import psutil` before that. # - BabelBrain.py imports its own siblings as top-level modules (`from # SelFiles.SelFiles import ...`) because it runs as a script with its own # directory first on sys.path. The launcher therefore runs the script; the # modules are not importable as `BabelBrain.SelFiles` and %%check does not # pretend otherwise. # - Compute devices are enumerated through CUDA (cupy, not in Fedora) and # OpenCL. With none found the first dialog says "BabelBrain can't run # without a GPU" and exits. pocl provides an OpenCL CPU device, so the # package Recommends it; stock python3-pyopencl needs pytools >= 2025.1.6 # (Bugzilla 2432626), which this Copr carries as python-pytools. %global commit 29b10261cf8b9fe0e8f3fc60762f805678db09ad %global shortcommit %(c=%{commit}; echo ${c:0:8}) %global snapdate 20261007 Name: babelbrain Version: 0.8.2 Release: 4.%{snapdate}git%{shortcommit}%{?dist} Summary: Planning of transcranial focused ultrasound (TUS) procedures License: BSD-3-Clause URL: https://github.com/ProteusMRIgHIFU/BabelBrain # main at 2026-10-07; BabelBrain/version.txt says 0.8.2. Upstream tags # releases irregularly and develops on the dualTx branch (0.8.8 prerelease). Source0: %{url}/archive/%{commit}/BabelBrain-%{shortcommit}.tar.gz # Use the elastix on PATH when the bundled binaries are absent (they are # removed here; our elastix package provides the command). Patch0: babelbrain-elastix-from-path.patch # The SimNIBS bridge scripts sourced a simnibs_env virtualenv unconditionally # and called `python`; use the system interpreter when there is no virtualenv. Patch1: babelbrain-simnibs-bridge-system-python.patch # sys.path additions relative to this file, not to the current directory. Patch2: babelbrain-sys-path-file-relative.patch # PlanTUS 2.0 renamed its flags (--skip_viewer, --placement_only) and its # per-vertex output files; follow both. Rebased onto Patch1 from the branch # submitted upstream by the packager. Patch3: babelbrain-plantus-2.0.patch # Default the SimNIBS root to /usr and the PlanTUS root to the plantus # package's tree when they are installed, instead of '...' (Fedora-specific). Patch4: babelbrain-fedora-tool-paths.patch # OfflineBatchExamples/UseEEGLocations/eeg_normals.py imported read_msh from # simnibs.mesh_tools, which SimNIBS 4 does not export; import it from simnibs. # Submitted upstream by the packager. Patch5: babelbrain-eeg-example-simnibs-import.patch BuildArch: noarch BuildRequires: python3-devel # %%check starts the GUI offscreen and needs an OpenCL device to get past the # file dialog: pocl's CPU device, through a pyopencl that can import. BuildRequires: pocl BuildRequires: python3-pyopencl >= 2026.1 # -3 is the first babelviscofdtd whose OpenCL backend imports without setuptools; # -4 the first whose OpenCL backend runs on numpy 2.4 (Step 2 on Linux). BuildRequires: python3-babelviscofdtd >= 1.2.7-4 BuildRequires: python3-pytools >= 2025.1.6 BuildRequires: coreutils BuildRequires: elastix # Coregistration of CT/ZTE/PETRA to the T1 (CTZTEProcessing.RunElastix). Requires: elastix # Step 1 intersects the skin mesh with the beam cone through trimesh's boolean # engine, which is manifold3d (not imported by BabelBrain itself, so the # generated requirements miss it; found by the Step 1 BATS test). Requires: python3-manifold3d # OpenCL on the CPU when there is no GPU driver; without any OpenCL or CUDA # device BabelBrain exits at its first dialog. Recommends: pocl # OpenCL on AMD and Intel GPUs through Mesa's rusticl (the launcher enables # it). On t2's Radeon Pro Vega 20 the solver's full wave check takes 22 s # against 10.5 min on pocl, with identical results. Step 2's Rayleigh # kernels need libclc with llvm-project#207264 (Fedora 44's 22.1.8-1 lacks # it; this Copr carries 22.1.8-1.fc44.1). Recommends: mesa-libOpenCL %if 0%{?fedora} == 44 # A GPU through rusticl must not get the broken libclc. Requires: (libclc-spirv >= 22.1.8-1.fc44.1 if mesa-libOpenCL) %endif # Stock Fedora python3-pyopencl 2025.2.6 cannot be imported (pytools floor # dropped, Bugzilla 2432626; no compyte subpackage). This Copr carries 2026.1.2 # and pytools 2026.1.1; the floors make sure those are what gets installed. Requires: python3-pyopencl >= 2026.1 Requires: python3-pytools >= 2025.1.6 Requires: python3-babelviscofdtd >= 1.2.7-4 # A TUS plan starts from a SimNIBS head model; ExternalBin/SimbNIBSMesh and # ExternalBin/PlanTUS run the system simnibs (Patch1). Recommends: python3-simnibs # Advanced Options > Run PlanTUS (transducer placement planning); Patch3 and # Patch4 make the packaged PlanTUS 2.0 work from that button. Recommends: plantus # Advanced Options > Force using Blender for CSG: trimesh's blender engine, # the fallback when manifold3d fails on a broken skin mesh. Suggests: blender # Fedora's PyWavelets is python-pywt; the generated python3dist(pywavelets) # requirement resolves to it. Nothing provides python3-pywavelets. %description BabelBrain is a graphical user interface for the planning of transcranial focused ultrasound (TUS) procedures. From a SimNIBS head segmentation, a T1 image and optionally a CT, ZTE or PETRA scan, a trajectory exported from Brainsight or 3D Slicer, and one of the supported transducers, it prepares the simulation domain (Step 1), computes the acoustic field through the skull with the BabelViscoFDTD solver (Step 2) and predicts heating and thermal dose for a sonication protocol (Step 3). Results are exported for neuronavigation. Compute backends on Linux are CUDA (needs cupy, not packaged) and OpenCL; the pocl package provides an OpenCL device on the CPU. %prep %autosetup -p1 -n BabelBrain-%{commit} # Upstream has no packaging metadata. Dependencies are the imports of the # three packages; the four not in Fedora (pymeshfix, pwlf, linetimer, # histoprint) are in this Copr. cupy, mlx and metalcomputebabel are CUDA/ # macOS-only and imported inside try blocks. cat > pyproject.toml <<'EOF' [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "BabelBrain" version = "%{version}" description = "Planning of transcranial focused ultrasound (TUS) procedures" authors = [{name = "Samuel Pichardo"}] license = {text = "BSD-3-Clause"} requires-python = ">=3.10" dependencies = [ "babelviscofdtd", "click", "et_xmlfile", "h5py", "hdf5plugin", "histoprint", "linetimer", "matplotlib", "networkx", "nibabel", "numba", "numpy", "numpy-stl", "openpyxl", "packaging", "pandas", "pillow", "platformdirs", "psutil", "pwlf", "py-cpuinfo", "pydicom", "pymeshfix", "pymupdf", "pyopencl", "pyside6", "pyvista", "pywavelets", "pyyaml", "requests", "scikit-image", "scipy", "simpleitk", "superqt", "trimesh", "vtk", "xmltodict", ] [tool.hatch.build.targets.wheel] packages = ["BabelBrain", "TranscranialModeling", "ThermalModeling"] exclude = [ "BabelBrain/ExternalBin/elastix/linux", "BabelBrain/ExternalBin/elastix/mac", "BabelBrain/ExternalBin/elastix/windows", "BabelBrain/ExternalBin/*/run_mac*.sh", "BabelBrain/ExternalBin/*/run_win.bat", "BabelBrain/BabelBrain.spec", "BabelBrain/BabelBrain.iss", "BabelBrain/BabelBrain-msi.json", "BabelBrain/entitlements.plist", "BabelBrain/create_*.sh", "BabelBrain/pycodesign.py", "BabelBrain/fix_app_qt_folder_names_for_codesign.py", ] EOF %generate_buildrequires %pyproject_buildrequires %build %pyproject_wheel %install %pyproject_install # hatchling records the three packages; pyproject_save_files wants the names. %pyproject_save_files BabelBrain TranscranialModeling ThermalModeling # Upstream runs the GUI as a script (its own directory first on sys.path). mkdir -p %{buildroot}%{_bindir} %if 0%{?fedora} == 44 %global rusticl_line export RUSTICL_ENABLE="\\${RUSTICL_ENABLE:-radeonsi,iris}" %else # libclc 23.1.2 (f45, rawhide) still fails to link sin/cos for rusticl, so # GPUs are not offered by default there; RUSTICL_ENABLE=radeonsi opts in. %global rusticl_line : %endif cat > %{buildroot}%{_bindir}/babelbrain <import.log 2>&1; then echo "IMPORT FAILED: $m"; tail -3 import.log; failed=1 fi done test $failed -eq 0 # The solver's OpenCL module must load (PropagationModel swallows its import # error into a "NOT loaded" line, which is how the GUI ends up with no device). %{python3} -c 'from BabelViscoFDTD.StaggeredFDTD_3D_With_Relaxation_OPENCL import ListDevices; d = ListDevices(); print("solver OpenCL devices:", d); assert d' # Now the application itself, the way the launcher starts it: offscreen, with # pocl's CPU device. It must enumerate an OpenCL device and reach the file # dialog, where it blocks until the timeout kills it (exit 124). A traceback # anywhere, or an empty device list, fails the build. This is the test that # was missing when 0.8.1-6 shipped unable to start. # Unbuffered: the process is killed, so a block-buffered stdout would lose the # device list. PYTHONUNBUFFERED=1 timeout -s TERM 120 %{python3} "$pkg/BabelBrain.py" > launcher.log 2>&1 && rc=0 || rc=$? cat launcher.log test $rc -eq 124 ! grep -q Traceback launcher.log grep -q "Available OPENCL Devices \['" launcher.log # The bridge scripts' patches applied and the elastix wrapper finds the # system binary. grep -q 'if \[ -f "\$1/simnibs_env/bin/activate" \]' "$pkg/ExternalBin/SimbNIBSMesh/run_linux.sh" grep -q 'if \[ -f "\$1/simnibs_env/bin/activate" \]' "$pkg/ExternalBin/PlanTUS/run_linux.sh" ! grep -qE '^python "' "$pkg"/ExternalBin/*/run_linux.sh "$pkg/ExternalBin/elastix/run_linux.sh" --help / / / 2>&1 | grep -q "elastix ended with code" grep -q '_HERE = os.path.dirname(os.path.abspath(__file__))' "$pkg/BabelBrain.py" grep -q -- '--skip_viewer' "$pkg/ExternalBin/PlanTUS/run_linux.sh" ! grep -q -- '--skip_wb_view' "$pkg/ExternalBin/PlanTUS/run_linux.sh" grep -q -- '--placement_only' "$pkg/PlanTUSViewer/RunPlanTUS.py" grep -q "/usr/share/plantus/PlanTUS_wrapper.py" "$pkg/Options/Options.py" ! grep -q "os.path.abspath('../')" "$pkg/BabelBrain.py" %files -f %{pyproject_files} %license LICENSE %doc %{_docdir}/%{name} %{_bindir}/babelbrain %changelog * Wed Oct 14 2026 Morgan Hough - 0.8.2-4.20261007git29b10261 - GPUs through Mesa's rusticl on Fedora 44: the launcher sets RUSTICL_ENABLE=radeonsi,iris unless the user set it, Recommends mesa-libOpenCL, and requires the Copr's fixed libclc (22.1.8-1.fc44.1) when mesa-libOpenCL is installed. On a Radeon Pro Vega 20 the solver's wave check runs in 22 s against 10.5 min on pocl, identical results; Step 1 on the GPU gives a mask identical to pocl's. Not enabled on f45 and rawhide, where libclc 23.1.2 still cannot link sin/cos for rusticl. * Tue Oct 13 2026 Morgan Hough - 0.8.2-3.20261007git29b10261 - Make Advanced Options > Run PlanTUS work with the new plantus package: follow PlanTUS 2.0's flags and output names (Patch3, from the branch submitted upstream), and default the PlanTUS root to /usr/share/plantus and the SimNIBS root to /usr when installed (Patch4). Recommends plantus. - Suggests blender for the Blender CSG option. - Fix the shipped UseEEGLocations example for SimNIBS 4 (Patch5): its eeg_normals.py died on `from simnibs.mesh_tools import read_msh`. - tests/babelbrain.bats: run the Advanced Options paths for real -- CT, ZTE, inverted ZTE and PETRA inputs (synthetic scans moved by a known rigid transform, judged by bone Dice against charm after elastix), grey/white matter segmentation, Blender CSG, the transducer calibration fit, and the EEG-locations example through Step 1 at Cz. * Mon Oct 12 2026 Morgan Hough - 0.8.2-2.20261007git29b10261 - Require python3-babelviscofdtd >= 1.2.7-4. With -3 every Step 2 simulation on Linux without CUDA died before a kernel ran: the OpenCL backend formats its kernel constants from 1-element arrays, which numpy 2.4 rejects (TypeError). Step 1 worked, so the BATS passed; the solver's own %%check and BATS now run a wave on the OpenCL backend. * Sun Oct 11 2026 Morgan Hough - 0.8.2-1.20261007git29b10261 - 0.8.1-6 had never run; every step below was measured in a mock root. - Ship TranscranialModeling and ThermalModeling (Steps 2 and 3); the wheel held only BabelBrain/. - Require what the code imports: psutil, py-cpuinfo, PyYAML (Fedora) and pymeshfix, pwlf, linetimer, histoprint (new packages in this Copr). The launcher died on `import psutil`; Step 1 on pymeshfix. - Require python3-manifold3d: trimesh's boolean engine, used by Step 1's skin-cone intersection; nothing imports it directly. - Patch0: ExternalBin/elastix/run_linux.sh runs the elastix on PATH when the bundled binary is absent; Requires: elastix. The -2 removal of the bundled binaries had left CT coregistration with nothing to run. - Patch1: the SimNIBS bridge scripts guard the virtualenv (was a sed in %%install). Patch2: sys.path from __file__, not the current directory. - Require python3-babelviscofdtd >= 1.2.7-3, whose OpenCL backend no longer needs distutils; with -2 the launcher found no device in a clean install. - Require python3-pyopencl >= 2026.1 and python3-pytools >= 2025.1.6 (both carried in this Copr; Fedora's pyopencl cannot be imported, Bugzilla 2432626) and Recommend pocl, so an OpenCL CPU device exists; with no device BabelBrain exits at its first dialog. - Version 0.8.2 as upstream's version.txt says; Source0 pinned to a commit instead of a moving main.tar.gz. noarch: nothing compiled remains. - %%check imports every module of the three steps and starts the GUI offscreen with pocl, requiring an OpenCL device and no traceback. - Profiles, OfflineBatchExamples, PlanningModels, PRIVACY.md installed as documentation. * Sat Sep 26 2026 Morgan Hough - 0.8.1-6 - Make the SimNIBS bridge usable from packages. ExternalBin/*/run_linux.sh unconditionally sourced a simnibs_env virtualenv that upstream's installer creates and a distribution install does not have, so the BabelBrain to SimNIBS workflow -- how a TUS plan actually begins -- failed before running anything. The activation is now conditional, falling through to the system interpreter where python3-simnibs already lives, and `python` is spelled python3 since there is no virtualenv to provide the unversioned name. - Add tests/babelbrain.bats. * Mon Sep 14 2026 Morgan Hough - 0.8.1-5 - Fix an unsatisfiable Requires that made the package uninstallable: Fedora's PyWavelets is python-pywt, and nothing provides the literal name "python3-pywavelets". Found by running gate 4 over the FUS stack -- babelbrain had been green in COPR since June while failing to install. - This does not make it installable on its own: python3-numba is still absent from Fedora entirely, and numba 0.67 needs llvmlite >= 0.49 where Fedora ships 0.46. That remains the blocker. * Thu Jun 11 2026 Morgan Hough - 0.8.1-4 - Refresh rebuild against the Python 3.15 rebuilds of its compute deps (python-babelviscofdtd 1.2.6-4, python-fmm3dpy 2.1.0-2) so rawhide resolves consistently. No spec/source changes. * Sun Feb 01 2026 Morgan Hough - 0.8.1-3 - Fix package summary length and spelling issues * Sun Feb 01 2026 Morgan Hough - 0.8.1-2 - Fix script permissions and clean up non-Linux scripts - Fix RPATHs for bundled elastix binaries - Disable debuginfo generation for bundled binaries * Sun Feb 01 2026 Morgan Hough - 0.8.1-1 - Initial package