Global fit
First, we load the required packages and include some Helper functions
include("helper_functions.jl")
load all T₁-mapping methods:
include("T1_mapping_methods.jl")
and initialize the plot:
p = plot([0, 2], [0, 2], xlabel="simulated T1 (s)", ylabel="literature T1 (s)", legend=:topleft, label=:none, xlim=(0.5, 1.1), ylim=(0.5, 1.1))
marker_list = [(seq_type_i == :IR) ? (:circle) : ((seq_type_i == :LL) ? (:cross) : ((seq_type_i == :vFA) ? (:diamond) : ((seq_type_i == :SR) ? (:dtriangle) : (:x)))) for seq_type_i in seq_type]
Mono-exponential model
We simulate the mono-exponential model as an MT model with a vanishing semi-solid spin pool. In this case, the underlying MT model is irrelevant, and we choose Graham's model for speed purposes:
MT_model = Graham()
The following parameters are hard-coded, except for R1f
, which serves as an initialization for the global fit.
m0s = 0
R1f = 1 / 1.084 # 1/s
R1s = R1f # 1/s
R2f = 1 / 0.0769 # 1/s
T2s = 0
Rx = 0 # 1/s
ω0 = 0
B1 = 1
We define a model for the global fit. It takes the global set of parameters p
as an input and returns a vector of T₁ estimates that correspond to the T₁ mapping methods described by the vector T1_functions
function model(iseq, p)
R1f = p[1]
T1 = similar(iseq, Float64)
Threads.@threads for i in eachindex(iseq)
T1[i] = T1_functions[iseq[i]](m0s, R1f, R2f, Rx, R1s, T2s)
end
return T1
end
Perform the fit and calculate the simulated T₁:
fit_mono = curve_fit(model, 1:length(T1_literature), T1_literature, [R1f], x_tol=1e-3)
T1_simulated = model(1:length(T1_literature), fit_mono.param)
For this model, the single fitted global parameter is:
T1f = 1 / fit_mono.param[1] # s
0.8894530882606787
The following plot visualizes the quality of the fit and replicates Fig. 1 in the manuscript:
scatter!(p, T1_simulated, T1_literature, label="mono-exponential model", markershape=marker_list, hover=seq_name)
Akaike (AIC) and Bayesian (BIC) information criteria
The information criteria depend on the number of measurements:
n = length(T1_literature)
25
the number of parameters:
k = length(fit_mono.param)
1
and the squared sum of the residuals:
RSS = norm(fit_mono.resid)^2
0.36165228007243183
As the AIC and BIC are only informative in the difference between two models, we use the mono-exponential model as the reference:
AIC_mono = n * log(RSS / n) + 2k
-103.8986976484162
BIC_mono = n * log(RSS / n) + k * log(n)
-102.679821823548
In this case, ΔAIC
is by definition zero:
ΔAIC = n * log(RSS / n) + 2k - AIC_mono
0.0
as is ΔBIC
:
ΔBIC = n * log(RSS / n) + k * log(n) - BIC_mono
0.0
Graham's MT model (unconstrained R₁ˢ)
MT_model = Graham()
The following parameters are hard-coded, except for m0s
, R1f
, and R1s
, which serve as an initialization for the global fit.
m0s = 0.25
R1f = 1 / 1.84 # 1/s
R1s = 1 / 0.34 # 1/s
R2f = 1 / 0.0769 # 1/s
T2s = 12.5e-6 # s
Rx = 13.6 # 1/s
ω0 = 0
B1 = 1
We define a model for the global fit. It takes the global set of parameters p
as an input and returns a vector of T₁ estimates that correspond to the T₁ mapping methods described by the vector T1_functions
function model(iseq, p)
m0s, R1f, R1s = p
T1 = similar(iseq, Float64)
Threads.@threads for i in eachindex(iseq)
T1[i] = T1_functions[iseq[i]](m0s, R1f, R2f, Rx, R1s, T2s)
end
return T1
end
Perform the fit and calculate the simulated T₁:
fit_Graham = curve_fit(model, 1:length(T1_literature), T1_literature, [m0s, R1f, R1s], x_tol=1e-3)
T1_simulated = model(1:length(T1_literature), fit_Graham.param)
For this model, the fitted global set of parameters is:
m0s = fit_Graham.param[1]
0.19446378469746337
T1f = 1 / fit_Graham.param[2] # s
2.026087604670749
T1s = 1 / fit_Graham.param[3] # s
0.24535283941180797
The following plot visualizes the quality of the fit and replicates Fig. 1 in the manuscript:
scatter!(p, T1_simulated, T1_literature, label="Graham's model (unconstrained R₁ˢ)", markershape=marker_list, hover=seq_name)
Note that clicking on a legend entry removes the fit from the plot. A double click on an entry selects only this particular fit.
Akaike (AIC) and Bayesian (BIC) information criteria
The information criteria depend on the number of measurements n
, the number of parameters k
, and the squared sum of the residuals RSS
:
n = length(T1_literature)
25
k = length(fit_Graham.param)
3
RSS = norm(fit_Graham.resid)^2
0.14656258643959397
With this information, we can calculate the AIC difference to the mono-exponential model:
ΔAIC = n * log(RSS / n) + 2k - AIC_mono
-18.580766237337997
and similarly for the BIC:
ΔBIC = n * log(RSS / n) + k * log(n) - BIC_mono
-16.1430145876016
Sled's MT model (unconstrained R₁ˢ)
MT_model = Sled()
The following parameters are hard-coded, except for m0s
, R1f
, and R1s
, which serve as an initialization for the global fit.
m0s = 0.25
R1f = 1 / 1.84 # 1/s
R1s = 1 / 0.34 # 1/s
R2f = 1 / 0.0769 # 1/s
T2s = 12.5e-6 # s
Rx = 13.6 # 1/s
ω0 = 0
B1 = 1
We define a model for the global fit. It takes the global set of parameters p
as an input and returns a vector of T₁ estimates that correspond to the T₁ mapping methods described by the vector T1_functions
function model(iseq, p)
m0s, R1f, R1s = p
T1 = similar(iseq, Float64)
Threads.@threads for i in eachindex(iseq)
T1[i] = T1_functions[iseq[i]](m0s, R1f, R2f, Rx, R1s, T2s)
end
return T1
end
Perform the fit and calculate the simulated T₁:
fit_Sled = curve_fit(model, 1:length(T1_literature), T1_literature, [m0s, R1f, R1s], x_tol=1e-3)
T1_simulated = model(1:length(T1_literature), fit_Sled.param)
For this model, the fitted global set of parameters is:
m0s = fit_Sled.param[1]
0.09938220860166001
T1f = 1 / fit_Sled.param[2] # s
1.7045103081206696
T1s = 1 / fit_Sled.param[3] # s
0.11768341516509102
The following plot visualizes the quality of the fit and replicates Fig. 1 in the manuscript:
scatter!(p, T1_simulated, T1_literature, label="Sled's model (unconstrained R₁ˢ)", markershape=marker_list, hover=seq_name)
Note that clicking on a legend entry removes the fit from the plot. A double click on an entry selects only this particular fit.
Akaike (AIC) and Bayesian (BIC) information criteria
The information criteria depend on the number of measurements n
, the number of parameters k
, and the squared sum of the residuals RSS
:
n = length(T1_literature)
25
k = length(fit_Sled.param)
3
RSS = norm(fit_Sled.resid)^2
0.13996520972762952
With this information, we can calculate the AIC difference to the mono-exponential model:
ΔAIC = n * log(RSS / n) + 2k - AIC_mono
-19.73223270329035
and similarly for the BIC:
ΔBIC = n * log(RSS / n) + k * log(n) - BIC_mono
-17.29448105355395
Generalized Bloch model (R₁ˢ = R₁ᶠ)
MT_model = gBloch()
The following parameters are hard-coded, except for m0s
and R1f
, which serve as an initialization for the global fit.
m0s = 0.139
R1f = 1 / 1.084 # 1/s
R1s = 1 # 1/s
R2f = 1 / 0.0769 # 1/s
T2s = 12.5e-6 # s
Rx = 23 # 1/s
ω0 = 0
B1 = 1
We define a model for the global fit. It takes the global set of parameters p
as an input and returns a vector of T₁ estimates that correspond to the T₁ mapping methods described by the vector T1_functions
function model(iseq, p)
m0s, R1f = p
R1s = R1f
T1 = similar(iseq, Float64)
Threads.@threads for i in eachindex(iseq)
T1[i] = T1_functions[iseq[i]](m0s, R1f, R2f, Rx, R1s, T2s)
end
return T1
end
Perform the fit and calculate the simulated T₁:
fit_constr = curve_fit(model, 1:length(T1_literature), T1_literature, [m0s, R1f], x_tol=1e-3)
T1_simulated = model(1:length(T1_literature), fit_constr.param)
For this model, the fitted global set of parameters is:
m0s = fit_constr.param[1]
0.12881793551863777
T1f = 1 / fit_constr.param[2] # s
0.9742290010278117
The following plot visualizes the quality of the fit and replicates Fig. 1 in the manuscript:
scatter!(p, T1_simulated, T1_literature, label="generalized Bloch model (R₁ˢ = R₁ᶠ constraint)", markershape=marker_list, hover=seq_name)
Note that clicking on a legend entry removes the fit from the plot. A double click on an entry selects only this particular fit.
Akaike (AIC) and Bayesian (BIC) information criteria
The information criteria depend on the number of measurements n
, the number of parameters k
, and the squared sum of the residuals RSS
:
n = length(T1_literature)
25
k = length(fit_constr.param)
2
RSS = norm(fit_constr.resid)^2
0.17054658474454984
With this information, we can calculate the AIC difference to the mono-exponential model:
ΔAIC = n * log(RSS / n) + 2k - AIC_mono
-16.791867855021792
and similarly for the BIC:
ΔBIC = n * log(RSS / n) + k * log(n) - BIC_mono
-15.5729920301536
Generalized Bloch model (unconstrained R₁ˢ)
MT_model = gBloch()
The following parameters are hard-coded, except for m0s
, R1f
, and R1s
, which serve as an initialization for the global fit.
m0s = 0.25
R1f = 1 / 1.84 # 1/s
R1s = 1 / 0.34 # 1/s
R2f = 1 / 0.0769 # 1/s
T2s = 12.5e-6 # s
Rx = 13.6 # 1/s
ω0 = 0
B1 = 1
We define a model for the global fit. It takes the global set of parameters p
as an input and returns a vector of T₁ estimates that correspond to the T₁ mapping methods described by the vector T1_functions
function model(iseq, p)
m0s, R1f, R1s = p
T1 = similar(iseq, Float64)
Threads.@threads for i in eachindex(iseq)
T1[i] = T1_functions[iseq[i]](m0s, R1f, R2f, Rx, R1s, T2s)
end
return T1
end
Perform the fit and calculate the simulated T₁:
fit_uncon = curve_fit(model, 1:length(T1_literature), T1_literature, [m0s, R1f, R1s], x_tol=1e-3)
T1_simulated = model(1:length(T1_literature), fit_uncon.param)
For this model, the fitted global set of parameters is:
m0s = fit_uncon.param[1]
0.20903106717408043
T1f = 1 / fit_uncon.param[2] # s
2.063020626243208
T1s = 1 / fit_uncon.param[3] # s
0.262550660513381
The following plot visualizes the quality of the fit and replicates Fig. 1 in the manuscript:
scatter!(p, T1_simulated, T1_literature, label="generalized Bloch model (unconstrained R₁ˢ)", markershape=marker_list, hover=seq_name)
Note that clicking on a legend entry removes the fit from the plot. A double click on an entry selects only this particular fit.
Akaike (AIC) and Bayesian (BIC) information criteria
The information criteria depend on the number of measurements n
, the number of parameters k
, and the squared sum of the residuals RSS
:
n = length(T1_literature)
25
k = length(fit_uncon.param)
3
RSS = norm(fit_uncon.resid)^2
0.11479629307483916
With this information, we can calculate the AIC difference to the mono-exponential model:
ΔAIC = n * log(RSS / n) + 2k - AIC_mono
-24.6881001209606
and similarly for the BIC:
ΔBIC = n * log(RSS / n) + k * log(n) - BIC_mono
-22.2503484712242
Data analysis
The span of T₁ estimates in the literature is
extrema(T1_literature)
(0.64, 1.0855)
and the coefficient of variation is
variation(T1_literature)
0.1408807728048427
Median absolute deviation
In the paper, the median absolute deviation from the mean value is used, as it is more robust to outliers compared to the mean absolute deviation or the standard deviation. Note, however, that the median absolute deviation of the mono-exponential fit is dominated by an outlier, artificially inflating the corresponding reduction.
A mono-exponential model explains the following fraction of the T₁ variability in the literature:
1 - mad(fit_mono.resid; center=mean(fit_mono.resid)) / mad(T1_literature; center=mean(T1_literature))
0.07118935312824093
Graham's spectral model without constraints on R₁ᶠ
explains the following fraction of the T₁ variability in the literature:
1 - mad(fit_Graham.resid; center=mean(fit_Graham.resid)) / mad(T1_literature; center=mean(T1_literature))
0.6071335860151669
Sled's model without constraints on R₁ᶠ
explains the following fraction of the T₁ variability in the literature:
1 - mad(fit_Sled.resid; center=mean(fit_Sled.resid)) / mad(T1_literature; center=mean(T1_literature))
0.5567822162017504
The generalized Bloch model constrained by R₁ˢ = R₁ᶠ
explains the following fraction of the T₁ variability in the literature:
1 - mad(fit_constr.resid; center=mean(fit_constr.resid)) / mad(T1_literature; center=mean(T1_literature))
0.4684203732531683
The generalized Bloch model without constraints on R₁ᶠ
explains the following fraction of the T₁ variability in the literature:
1 - mad(fit_uncon.resid; center=mean(fit_uncon.resid)) / mad(T1_literature; center=mean(T1_literature))
0.6171961683507089
Mean absolute deviation
For comparison, the mean absolute deviation is analyzed: A mono-exponential model explains the following fraction of the T₁ variability in the literature:
1 - mean(abs.(fit_mono.resid .- mean(fit_mono.resid))) / mean(abs.(T1_literature .- mean(T1_literature)))
0.023426403041203026
Graham's spectral model without constraints on R₁ᶠ
explains the following fraction of the T₁ variability in the literature:
1 - mean(abs.(fit_Graham.resid .- mean(fit_Graham.resid))) / mean(abs.(T1_literature .- mean(T1_literature)))
0.4834831199140681
Sled's model without constraints on R₁ᶠ
explains the following fraction of the T₁ variability in the literature:
1 - mean(abs.(fit_Sled.resid .- mean(fit_Sled.resid))) / mean(abs.(T1_literature .- mean(T1_literature)))
0.48265447199825884
The generalized Bloch model constrained by R₁ˢ = R₁ᶠ
explains the following fraction of the T₁ variability in the literature:
1 - mean(abs.(fit_constr.resid .- mean(fit_constr.resid))) / mean(abs.(T1_literature .- mean(T1_literature)))
0.42795873321794164
The generalized Bloch model without constraints on R₁ᶠ
explains the following fraction of the T₁ variability in the literature:
1 - mean(abs.(fit_uncon.resid .- mean(fit_uncon.resid))) / mean(abs.(T1_literature .- mean(T1_literature)))
0.5261587981865726
Standard deviation
For comparison, the standard deviation is analyzed: A mono-exponential model explains the following fraction of the T₁ variability in the literature:
1 - std(fit_mono.resid) / std(T1_literature)
0.01826093355907199
Graham's spectral model without constraints on R₁ᶠ
explains the following fraction of the T₁ variability in the literature:
1 - std(fit_Graham.resid) / std(T1_literature)
0.37502858305844344
Sled's spectral model without constraints on R₁ᶠ
explains the following fraction of the T₁ variability in the literature:
1 - std(fit_Sled.resid) / std(T1_literature)
0.38924942271187124
The generalized Bloch model constrained by R₁ˢ = R₁ᶠ
explains the following fraction of the T₁ variability in the literature:
1 - std(fit_constr.resid) / std(T1_literature)
0.32587111172659133
The generalized Bloch model without constraints on R₁ᶠ
explains the following fraction of the T₁ variability in the literature:
1 - std(fit_uncon.resid) / std(T1_literature)
0.4468849451044564