- Config name
model: qmt_spgrBIDS suffixQMTSPGRMethod family- Quantitative MT Acquired volumes
- 10 Fit granularity
- voxelwise
Two-pool quantitative magnetization transfer from a spoiled gradient-echo sequence with off-resonance saturation sampled across a grid of saturation flip angles and frequency offsets. A free-water pool exchanges magnetization with a restricted macromolecular pool; fitting the sampled Z-spectrum recovers the bound-pool fraction and the exchange rate. Two steady-state solutions are available — Ramani’s closed form and the Sled–Pike rectangular-pulse approximation — and B1, B0 and R1 maps constrain the fit when supplied.
Signal model¶
| Symbol | Quantity | Unit |
|---|---|---|
F | Bound-pool fraction M0r/M0f | — |
kr | Exchange rate, restricted to free pool | 1/s |
R1f | Free-pool longitudinal relaxation rate | 1/s |
R1r | Restricted-pool longitudinal relaxation rate | 1/s |
T2f | Free-pool transverse relaxation time | s |
T2r | Restricted-pool transverse relaxation time | s |
What the model reads¶
A series of 10 volumes indexed by Angle, Offset. Volumes are matched to these identities by value, so reordering the acquisition yields an identical fit.
| # | Angle | Offset |
|---|---|---|
| 1 | 142 | 443 |
| 2 | 426 | 443 |
| 3 | 142 | 1088 |
| 4 | 426 | 1088 |
| 5 | 142 | 2732 |
| 6 | 426 | 2732 |
| 7 | 142 | 6862 |
| 8 | 426 | 6862 |
| 9 | 142 | 17235 |
| 10 | 426 | 17235 |
Where the protocol comes from¶
In a BIDS dataset these values are resolved for you:
| Protocol parameter | Source | Key | Resolved |
|---|---|---|---|
Angle | sidecar field | Angle | per volume |
Offset | sidecar field | Offset | per volume |
Inputs¶

Figure 1:The 10 acquired volumes, each labeled with the identity the model reads it by.

Figure 2:Auxiliary maps and the mask applied to this fit.
Auxiliary maps¶
| Input | Required | BIDS suffix | CLI flag |
|---|---|---|---|
R1map | optional | R1map | --r1map |
B1map | optional | TB1map | --b1map |
B0map | optional | B0map | --b0map |
Outputs¶
| Map | BIDS suffix | Unit | Fit bounds | Fitted |
|---|---|---|---|---|
F | Fmap | — | 0.0001 … 0.5 | free |
kr | kRmap | 1/s | 0.0001 … 100 | free |
R1f | R1Fmap | 1/s | 0.05 … 5 | fixed |
R1r | R1Rmap | 1/s | 0.05 … 5 | fixed |
T2f | T2Fmap | s | 0.003 … 0.5 | free |
T2r | T2Rmap | s | 3e-06 … 5e-05 | free |
Diagnostic outputs
Reported by the fit but not written as BIDS derivatives: they describe the fit rather than the tissue, and have no BIDS suffix to be written under.
| Output |
|---|
kf |
resnorm |

Figure 3:Fitted maps for one example subject.
Usage¶
The acquisition comes from the sidecars, so recipes/bids/qmt_config_ramani.yaml carries only algorithm options:
# qMT-SPGR (Ramani) recipe for `qmrust fit --bids-dir`.
# The acquisition table (per-volume Angle/Offset) is NOT listed here: it is
# read from the BIDS sidecars via the model's protocol_schema() and composed
# into the fit. Only algorithm options + fixed timing/pulse/fitting live here.
# Aux maps (R1map/B1map/B0map) are resolved from the dataset by suffix.
model: qmt_spgr
qmt_spgr:
protocol:
timing:
tmt: 0.0102
TR: 0.025
pulse:
shape: gausshann
bandwidth: 200.0
lineshape: SuperLorentzian
model: Ramani
read_pulse_alpha: 7.0
fitting:
st:
- 0.16
- 30.0
- 1.0
- 1.0
- 0.03
- 0.000013
lb:
- 0.0001
- 0.0001
- 0.05
- 0.05
- 0.003
- 3e-6
ub:
- 0.5
- 100.0
- 5.0
- 5.0
- 0.5
- 0.00005
fx:
- false
- false
- true
- true
- false
- false
use_r1map_to_constrain_r1f: true
fix_r1r_eq_r1f: false
fix_r1f_t2f: false
r1f_t2f: 0.055
# BIDS mask selection: restrict the fit to this dataset mask (same as the IR
# recipe). Remove this block to fit the whole volume.
mask:
desc: brainqmrust fit --bids-dir ds-mydata \
--config recipes/bids/qmt_config_ramani.yaml --output-dir outFitted maps are written as BIDS derivatives:
out/derivatives/qmrust/sub-01/anat/
sub-01_Fmap.nii.gz
sub-01_Fmap.json
sub-01_kRmap.nii.gz
sub-01_kRmap.json
sub-01_R1Fmap.nii.gz
sub-01_R1Fmap.json
sub-01_R1Rmap.nii.gz
sub-01_R1Rmap.json
sub-01_T2Fmap.nii.gz
sub-01_T2Fmap.json
sub-01_T2Rmap.nii.gz
sub-01_T2Rmap.jsonNo sidecars, so recipes/non-bids/qmt_config_ramani.yaml declares the acquisition itself:
# Fully-resolved config (all defaults materialized, validation applied).
# Regenerate with: qmrust dump-config --config qmt_config_ramani.yaml
model: qmt_spgr
qmt_spgr:
protocol:
mtdata:
- - 142.0
- 443.0
- - 426.0
- 443.0
- - 142.0
- 1088.0
- - 426.0
- 1088.0
- - 142.0
- 2732.0
- - 426.0
- 2732.0
- - 142.0
- 6862.0
- - 426.0
- 6862.0
- - 142.0
- 17235.0
- - 426.0
- 17235.0
timing:
tmt: 0.0102
TR: 0.025
pulse:
shape: gausshann
bandwidth: 200.0
lineshape: SuperLorentzian
model: Ramani
read_pulse_alpha: 7.0
fitting:
st:
- 0.16
- 30.0
- 1.0
- 1.0
- 0.03
- 0.000013
lb:
- 0.0001
- 0.0001
- 0.05
- 0.05
- 0.003
- 3e-6
ub:
- 0.5
- 100.0
- 5.0
- 5.0
- 0.5
- 0.00005
fx:
- false
- false
- true
- true
- false
- false
use_r1map_to_constrain_r1f: true
fix_r1r_eq_r1f: false
fix_r1f_t2f: false
r1f_t2f: 0.055
mask:
desc: brainqmrust fit --data data.nii.gz --mask mask.nii.gz \
--config recipes/non-bids/qmt_config_ramani.yaml \
--r1map <file> \
--b1map <file> \
--b0map <file> \
--output-dir outSee Fitting without BIDS.
model: qmt_spgr
qmt_spgr:
model: Ramani
fitting:
use_r1map_to_constrain_r1f: false
fx: [false, false, true, true, false, false]
sim:
params: { F: 0.15, kr: 25.0, R1f: 1.0, R1r: 1.0, T2f: 0.028, T2r: 1.1e-5 }
b1: 1.0
b0: 0.0
noise: { type: rician, snr: 100.0 }
seed: 0
trials: 100
sweep: { param: F, start: 0.05, stop: 0.30, steps: 10 }
distributions:
F: { mean: 0.15, std: 0.02 }
kr: { mean: 25.0, std: 3.0 }qmrust sim signal --config recipes/sim/qmt_sim_ramani.yaml --output signal.json
qmrust sim single-voxel --config recipes/sim/qmt_sim_ramani.yaml --output sv.json
qmrust sim sensitivity --config recipes/sim/qmt_sim_ramani.yaml --output sens.json
qmrust sim montecarlo --config recipes/sim/qmt_sim_ramani.yaml --output mc.jsonSee Simulation.
The same core, compiled to WebAssembly:
import init, { fit_volume } from './pkg/qmrust_wasm.js';
await init();
const maps = fit_volume(configYaml, data, [nx, ny, nz, nt],
JSON.stringify(volumeIds), mask, '{}');Try it on real data in the playground, or read Browser & wasm.

Figure 4:One voxel’s measured data with the model’s forward signal at the fitted parameters. The curve is computed by the same Rust code that does the fitting.
Implementation¶
model.rs— theModelimplementation: measurement contract, protocol schema, BIDS outputs.fit.rs— the numerical fit.config.rs— configuration and validation.
Fully-resolved configuration
Every default materialized, as qmrust dump-config prints it.
model: qmt_spgr
qmt_spgr:
protocol:
mtdata:
- - 142.0
- 443.0
- - 426.0
- 443.0
- - 142.0
- 1088.0
- - 426.0
- 1088.0
- - 142.0
- 2732.0
- - 426.0
- 2732.0
- - 142.0
- 6862.0
- - 426.0
- 6862.0
- - 142.0
- 17235.0
- - 426.0
- 17235.0
timing:
tmt: 0.0102
TR: 0.025
pulse:
shape: gausshann
bandwidth: 200.0
lineshape: SuperLorentzian
model: Ramani
read_pulse_alpha: 7.0
fitting:
st:
- 0.16
- 30.0
- 1.0
- 1.0
- 0.03
- 0.000013
lb:
- 0.0001
- 0.0001
- 0.05
- 0.05
- 0.003
- 3e-6
ub:
- 0.5
- 100.0
- 5.0
- 5.0
- 0.5
- 0.00005
fx:
- false
- false
- true
- true
- false
- false
use_r1map_to_constrain_r1f: true
fix_r1r_eq_r1f: false
fix_r1f_t2f: false
r1f_t2f: 0.055References¶
Ramani et al. (2002) Sled & Pike (2001) Cabana et al. (2015)
- Ramani, A., Dalton, C., Miller, D. H., Tofts, P. S., & Barker, G. J. (2002). Precise estimate of fundamental in-vivo MT parameters in human brain in clinically feasible times. Magnetic Resonance Imaging, 20(10), 721–731.
- Sled, J. G., & Pike, G. B. (2001). Quantitative imaging of magnetization transfer exchange and relaxation properties in vivo using MRI. Magnetic Resonance in Medicine, 46(5), 923–931.
- Cabana, J.-F., Gu, Y., Boudreau, M., Levesque, I. R., Atchia, Y., Sled, J. G., Narayanan, S., Arnold, D. L., Pike, G. B., Cohen-Adad, J., Duval, T., Vuong, M.-T., & Stikov, N. (2015). Quantitative magnetization transfer imaging made easy with qMTLab: Software for data simulation, analysis, and visualization. Concepts in Magnetic Resonance Part A, 44(5), 263–277.