Skip to content

Fit mobafit's water/fat, diffusion and Z-spectrum models with TorchSim - #112

Merged
mcencini merged 17 commits into
mainfrom
claude/bart-signal-models-torchsim-87edyf
Sep 30, 2026
Merged

mcencini merged 17 commits into
mainfrom
claude/bart-signal-models-torchsim-87edyf

Conversation

@mcencini

@mcencini mcencini commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Requested by Matteo · project thread

What changed

Before: bartorch mobafit went through the app only for -T, -I and -L. Water/fat (-G), diffusion (-D) and Lorentzian Z-spectra (-M) ran as the BART command.

After: those three are also fitted by apps.mobafit, using TorchSim's closed forms. They come out in BART's coefficient layout and units:

  • For -G (models 0, 1, 3 and 4), a complex amplitude with a fat fraction and a fat phase is mapped back to W, F, R2* and fB0.
  • For -D, the model is TorchSim's diffusion decay.
  • For -M, the model is TorchSim's Lorentzian lines; -M goes through the app when --init is given.

The following still run as the command:

  • -G -m 2, whose separate water and fat decays do not converge in the bounded fit.
  • -P.
  • -M without a start.
  • --fB0-init.

TorchSim's floor is raised to 0.0.7, the first release with these simulators (FiRMLAB-Pisa/torchsim#20).

A failed BART command no longer hangs the process

Before: on Windows the suite hung partway through. BART's Windows vdprintf opened a stdio stream per call and never closed it, so after a few dozen CFL files create_cfl failed. On every platform, an error raised inside an OpenMP critical section left that section locked, so the next command waited on it forever. This happened after a bad option or -R term, or a missing input.

After:

  • BART is pinned to the downstream merge of pulserver/bart#5, which writes through _write.
  • src/csrc/substitute/critical.c defines the runtime's critical-section entry points (__kmpc_critical for clang, GOMP_critical_* for GCC) hidden inside the library. Each forwards to the runtime the library is bound to and records per thread which sections are held.
  • bartorch_command and guarded leave whatever is still held once BART's error catcher returns.
  • tests/test_core.py runs each of those three failures followed by two good commands in a child process with a timeout. Without the change it times out.

Why

Signal simulation belongs to TorchSim rather than to BART. tests/test_signal_models.py pins every closed-form model BART ships, from signal and mobasig, to its TorchSim simulator within single-precision round-off. The models covered are Look-Locker (three variants), MOLLI, IR-bSSFP, spin echo and IR spin echo, the TSE generating function, water/fat, ASL (continuous and pulsed), the five multi-echo models, the Middleton fat spectrum at 1.5 T, diffusion, and Lorentzian Z-spectra.

Validation

  • ./scripts/lint.sh: all checks passed, codespell included.
  • Full suite against a clang build of libbartorch.so: 2304 passed, 132 skipped, 2 failed.
    • The 2 failures are tests/test_openmp.py, which fails locally only because the clang build loads LLVM's libomp next to torch's libgomp. CI builds with GCC 14.
  • The interposer was also exercised in a standalone harness against GCC 13's libgomp and clang 18's libomp. The harness jumps out of a named and an unnamed section and re-enters on the same thread, then enters from four threads. It hangs without the interposer and passes with it.
  • The new mobafit routes are held against the command in tests/test_cli.py, to the tolerance the existing -T/-I/-L routes use.
  • No CUDA was available.

Public API and documentation

There are no new public names. docs/api/cli.md and AGENTS.md now list the new routes and what still goes to BART, and docs/guides/user/prerequisites.md names TorchSim 0.0.7. AGENTS.md also describes the critical-section release under Errors.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FavM1ptGCKq51oher8tVd9


Generated by Claude Code

Each closed-form model the signal tool and mobasig compute is held to
single-precision round-off against its TorchSim simulator: Look-Locker in
three variants, MOLLI, IR-bSSFP, spin echo with and without inversion,
the stimulated-echo train, water and fat in all five multi-echo models
and both fat spectra, diffusion, the Lorentzian Z-spectrum and both ASL
labellings. The module skips on a TorchSim without them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FavM1ptGCKq51oher8tVd9
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FavM1ptGCKq51oher8tVd9
…e app

-G (WF, WFR2S, R2S, PHASEDIFF), -D and -M with --init are fitted with
TorchSim's closed forms; -G -m 2, -P and -M without a start stay on BART.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FavM1ptGCKq51oher8tVd9
@codecov

codecov Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Fits from --init for -G -m 0, 1 and 3, and the routes to the command for
a start with no fat, -M without a start and -m 5.  -m 2's refusal is
checked before the model table, which does not carry it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FavM1ptGCKq51oher8tVd9
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FavM1ptGCKq51oher8tVd9
The bisection steps, their scripts and the faulthandler timeout, whose
dump went to pytest's capture rather than the log.  The hour's limit on
a test job stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FavM1ptGCKq51oher8tVd9
On Windows BART's vdprintf opened a stream over the descriptor for every
call and never closed it, and a CFL header is a few dozen calls, so a
process that had written a few dozen files through BART's commands ran
out of the C runtime's streams.  The next create_cfl failed inside its
OpenMP critical section, BART's error left the section held, and the
next command waited on it: the Windows suite hung after test_cli's
magnitude mobafit, in the FINUFFT agreement check conftest runs.

The fork now formats into memory and writes with _write
(pulserver/bart da80f03f).  test_cli writes sixty-four files through
the command in one process.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FavM1ptGCKq51oher8tVd9
BART's error leaves a command by a long jump, and a jump out of an OpenMP
critical section skips the call that leaves it.  An unknown option or
regularization term is raised inside the section BART parses options in,
a missing input inside the one every file is loaded in, and the next
command to enter either one waited for ever.

src/csrc/substitute/critical.c defines the runtime's critical-section
entry points, clang's __kmpc_critical family and GCC's GOMP_critical_*,
hidden inside the library.  Each forwards to the runtime the library is
bound to and records per thread the sections held.  bartorch_command and
guarded leave whatever is still held once the catch returns, and a thread
entering a section it already holds leaves the stale one first, which
BART's cleanup after a failed command needs.

A test in test_core runs each of the three failures and then two good
commands in a child process with a timeout; without this change it times
out.  cli._missing stays, for the app route, which reads its inputs in
Python.

BART is re-pinned to the merge of pulserver/bart#5 on downstream, the
same tree as the commit pinned before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FavM1ptGCKq51oher8tVd9
@mcencini
mcencini marked this pull request as ready for review September 30, 2026 17:29
@mcencini
mcencini merged commit a27fc2d into main Sep 30, 2026
21 of 22 checks passed
@mcencini
mcencini deleted the claude/bart-signal-models-torchsim-87edyf branch September 30, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants