Skip to content

Commit 3fa5a43

Browse files
committed
Test simulation waveform event types
1 parent 9d52e62 commit 3fa5a43

1 file changed

Lines changed: 86 additions & 0 deletions

File tree

KomaMRICore/test/runtests.jl

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,92 @@ end
247247
end
248248
end
249249

250+
@testitem "Bloch waveform event type accuracy" tags=[:core, :nomotion] begin
251+
using OrdinaryDiffEqTsit5
252+
include("initialize_backend.jl")
253+
include(joinpath(@__DIR__, "test_files", "utils.jl"))
254+
255+
Tpulse = 1e-3
256+
Tgrad = 1e-3
257+
Tadc = 1e-3
258+
Nadc = 6
259+
M0 = 1.0
260+
T1 = 1000e-3
261+
T2 = 40e-3
262+
Δw = 2π * 30
263+
x0 = 1e-2
264+
B1 = 1.5e-6 * cis/ 7)
265+
Gx = 0.2e-3
266+
267+
sys = Scanner()
268+
obj = Phantom(x=[x0], ρ=[M0], T1=[T1], T2=[T2], Δw=[Δw])
269+
270+
waveform_events = (
271+
"trap/block" => (Grad(Gx, Tgrad), RF(B1, Tpulse)),
272+
"uniform" => (Grad([Gx, Gx], Tgrad), RF([B1, B1], Tpulse)),
273+
"time-shaped" => (Grad([Gx, Gx], [Tgrad]), RF([B1, B1], [Tpulse])),
274+
)
275+
276+
function waveform_sequence(grad, rf)
277+
no_grad = Grad(0.0, 0.0)
278+
no_rf = RF(0.0, 0.0)
279+
no_adc = ADC(0, 0.0)
280+
seq = Sequence(
281+
[no_grad grad; no_grad no_grad; no_grad no_grad],
282+
[rf no_rf],
283+
[no_adc, no_adc],
284+
)
285+
seq += ADC(Nadc, Tadc)
286+
return seq
287+
end
288+
289+
function diffeq_signal(seq)
290+
B1e(t) = KomaMRIBase.get_rfs(seq, [t])[1][1]
291+
Gxe(t) = KomaMRIBase.get_grads(seq, [t])[1][1]
292+
function bloch!(dm, m, p, t)
293+
mx, my, mz = m
294+
B1t = B1e(t)
295+
bx, by, bz = real(B1t), imag(B1t), x0 * Gxe(t) + Δw / (2π * γ)
296+
dm[1] = -mx / T2 + 2π * γ * bz * my - 2π * γ * by * mz
297+
dm[2] = -2π * γ * bz * mx - my / T2 + 2π * γ * bx * mz
298+
dm[3] = 2π * γ * by * mx - 2π * γ * bx * my - mz / T1 + M0 / T1
299+
return nothing
300+
end
301+
sol = solve(
302+
ODEProblem(bloch!, [0.0, 0.0, M0], (0.0, dur(seq))),
303+
Tsit5();
304+
saveat=get_adc_sampling_times(seq),
305+
tstops=[Tpulse, Tpulse + Tgrad],
306+
dtmax=1e-6,
307+
abstol=1e-9,
308+
reltol=1e-9,
309+
)
310+
sol_diffeq = hcat(sol.u...)'
311+
return sol_diffeq[:, 1] + im * sol_diffeq[:, 2]
312+
end
313+
314+
ref_adc_times = get_adc_sampling_times(waveform_sequence(Grad(Gx, Tgrad), RF(B1, Tpulse)))
315+
316+
for (name, events) in waveform_events
317+
seq = waveform_sequence(events...)
318+
@test get_adc_sampling_times(seq) ref_adc_times
319+
mxy_diffeq = diffeq_signal(seq)
320+
for sim_method in (Bloch(), BlochMagnus1(), BlochMagnus2(), BlochMagnus4())
321+
@testset "$name $(nameof(typeof(sim_method)))" begin
322+
sim_params = Dict{String, Any}(
323+
"gpu" => USE_GPU,
324+
"return_type" => "mat",
325+
"sim_method" => sim_method,
326+
"Δt" => 1e-5,
327+
"Δt_rf" => 1e-5,
328+
)
329+
raw = simulate(obj, seq, sys; sim_params, verbose=false)[:, 1, 1]
330+
@test NRMSE(raw, mxy_diffeq) < 0.1
331+
end
332+
end
333+
end
334+
end
335+
250336
@testitem "Bloch_RF_accuracy" tags=[:important, :core, :nomotion] begin
251337
using OrdinaryDiffEqTsit5
252338
include("initialize_backend.jl")

0 commit comments

Comments
 (0)