Skip to content

Commit 295d3a2

Browse files
committed
Test simulation waveform event types
1 parent 9d52e62 commit 295d3a2

1 file changed

Lines changed: 85 additions & 0 deletions

File tree

KomaMRICore/test/runtests.jl

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,91 @@ 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+
grad_events = (
271+
"trap" => Grad(Gx, Tgrad),
272+
"uniform" => Grad([Gx, Gx], Tgrad),
273+
"time-shaped" => Grad([Gx, Gx], [Tgrad]),
274+
)
275+
rf_events = (
276+
"block" => RF(B1, Tpulse),
277+
"uniform" => RF([B1, B1], Tpulse),
278+
"time-shaped" => RF([B1, B1], [Tpulse]),
279+
)
280+
281+
function waveform_sequence(grad, rf)
282+
seq = Sequence()
283+
@addblock seq += rf + (ADC(Nadc, Tadc), x=grad)
284+
return seq
285+
end
286+
287+
function diffeq_signal(seq)
288+
B1e(t) = KomaMRIBase.get_rfs(seq, [t])[1][1]
289+
Gxe(t) = KomaMRIBase.get_grads(seq, [t])[1][1]
290+
function bloch!(dm, m, p, t)
291+
mx, my, mz = m
292+
B1t = B1e(t)
293+
bx, by, bz = real(B1t), imag(B1t), x0 * Gxe(t) + Δw / (2π * γ)
294+
dm[1] = -mx / T2 + 2π * γ * bz * my - 2π * γ * by * mz
295+
dm[2] = -2π * γ * bz * mx - my / T2 + 2π * γ * bx * mz
296+
dm[3] = 2π * γ * by * mx - 2π * γ * bx * my - mz / T1 + M0 / T1
297+
return nothing
298+
end
299+
sol = solve(
300+
ODEProblem(bloch!, [0.0, 0.0, M0], (0.0, dur(seq))),
301+
Tsit5();
302+
saveat=get_adc_sampling_times(seq),
303+
tstops=[Tpulse, Tpulse + Tgrad],
304+
dtmax=1e-6,
305+
abstol=1e-9,
306+
reltol=1e-9,
307+
)
308+
sol_diffeq = hcat(sol.u...)'
309+
return sol_diffeq[:, 1] + im * sol_diffeq[:, 2]
310+
end
311+
312+
ref_seq = waveform_sequence(grad_events[1][2], rf_events[1][2])
313+
ref_adc_times = get_adc_sampling_times(ref_seq)
314+
mxy_diffeq = diffeq_signal(ref_seq)
315+
316+
for (grad_name, grad) in grad_events, (rf_name, rf) in rf_events
317+
seq = waveform_sequence(grad, rf)
318+
@test get_adc_sampling_times(seq) ref_adc_times
319+
for sim_method in (Bloch(), BlochMagnus1(), BlochMagnus2(), BlochMagnus4())
320+
@testset "$grad_name/$rf_name $(nameof(typeof(sim_method)))" begin
321+
sim_params = Dict{String, Any}(
322+
"gpu" => USE_GPU,
323+
"return_type" => "mat",
324+
"sim_method" => sim_method,
325+
"Δt" => 1e-5,
326+
"Δt_rf" => 1e-5,
327+
)
328+
raw = simulate(obj, seq, sys; sim_params, verbose=false)[:, 1, 1]
329+
@test NRMSE(raw, mxy_diffeq) < 0.1
330+
end
331+
end
332+
end
333+
end
334+
250335
@testitem "Bloch_RF_accuracy" tags=[:important, :core, :nomotion] begin
251336
using OrdinaryDiffEqTsit5
252337
include("initialize_backend.jl")

0 commit comments

Comments
 (0)