In summary, we should review the following:
Description
While testing a phantom with 1-spin and 90-degree RF pulse, given by the following script:
# Import modules
using KomaMRI
# Define the scanner struct
sys = Scanner()
# Define the 1-spin phantom struct with T1 and T2 parameters
T1, T2 = 1, .1
obj = Phantom(x=[0], T1=[T1], T2=[T2])
# Define the 90-degree hard excitation pulse parameters
ampRF = sys.B1 # amplitude of the RF pulse
durRF = π / 2 / (2π * γ * ampRF) # duration of the RF pulse
# Define the 90-degree hard excitation pulse parameters
nADC = 16 # number of acquisition samples
durADC = 5 * T2 # duration of the acquisition
# Define sequence struct
matGRs = [Grad(0, durRF) Grad(0, durADC);
Grad(0, durRF) Grad(0, durADC);
Grad(0, durRF) Grad(0, durADC)]
matRFs = [RF(ampRF, durRF) RF(0, durADC)]
vecADCs = [ADC(0, durRF); ADC(nADC, durADC)]
seq = Sequence(matGRs, matRFs, vecADCs)
# Plot the sequence
p = plot_seq(seq; slider=true, height=300);
savefig(p, "seq.html");
# Perform the simulation and get the output raw signal
sig = simulate(obj, seq, sys)
, the simulation()
function throws an error related with the Interpolation:
julia> sig = simulate(obj, seq, sys)
[ Info: Running simulation... [GPU = true, CPU = 1 thread(s)].
Dividing simulation in Nblocks=4
Starting simulation with Nspins=1 and Nt=515
ERROR: BoundsError: attempt to access 515-element extrapolate(interpolate((::Vector{Float64},), ::Vector{ComplexF64}, Gridded(Linear())), Throw()) with element type ComplexF64 at index [0.500587165012496]
Stacktrace:
[1] throw_boundserror(A::Interpolations.Extrapolation{ComplexF64, 1, Interpolations.GriddedInterpolation{ComplexF64, 1, ComplexF64, Interpolations.Gridded{Interpolations.Linear{Interpolations.Throw{Interpolations.OnGrid}}}, Tuple{Vector{Float64}}}, Interpolations.Gridded{Interpolations.Linear{Interpolations.Throw{Interpolations.OnGrid}}}, Interpolations.Throw{Nothing}}, I::Tuple{Float64})
@ Base .\abstractarray.jl:691
[2] inbounds_index
@ C:\Users\beorostica\.julia\packages\Interpolations\y4lLj\src\extrapolation\extrapolation.jl:111 [inlined]
[3] inbounds_position
@ C:\Users\beorostica\.julia\packages\Interpolations\y4lLj\src\extrapolation\extrapolation.jl:102 [inlined]
[4] (::Interpolations.Extrapolation{ComplexF64, 1, Interpolations.GriddedInterpolation{ComplexF64, 1, ComplexF64, Interpolations.Gridded{Interpolations.Linear{Interpolations.Throw{Interpolations.OnGrid}}}, Tuple{Vector{Float64}}}, Interpolations.Gridded{Interpolations.Linear{Interpolations.Throw{Interpolations.OnGrid}}}, Interpolations.Throw{Nothing}})(x::Float64)
@ Interpolations C:\Users\beorostica\.julia\packages\Interpolations\y4lLj\src\extrapolation\extrapolation.jl:48
[5] Extrapolation
@ C:\Users\beorostica\.julia\packages\Interpolations\y4lLj\src\extrapolation\extrapolation.jl:61 [inlined]
[6] run_sim_time_iter(obj::Phantom, seq::Sequence, t::Vector{Float64}, Δt::Vector{Float64}; Nblocks::Int64, Nthreads::Int64, gpu::Function, w::Nothing)
@ KomaMRI C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\simulation\SimulatorCore.jl:200
[7] macro expansion
@ .\timing.jl:220 [inlined]
[8] simulate(obj::Phantom, seq::Sequence, sys::Scanner; simParams::Dict{String, Any}, w::Nothing)
@ KomaMRI C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\simulation\SimulatorCore.jl:228
[9] simulate(obj::Phantom, seq::Sequence, sys::Scanner)
@ KomaMRI C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\simulation\SimulatorCore.jl:206
[10] top-level scope
@ REPL[430]:1
[11] top-level scope
@ C:\Users\beorostica\.julia\packages\CUDA\DfvRa\src\initialization.jl:52
This problem can be overcome by adding a gradient with amplitude different from zero at the end of the sequence, like in the following script:
# Import modules
using KomaMRI
# Define the scanner struct
sys = Scanner()
# Define the 1-spin phantom struct with T1 and T2 parameters
T1, T2 = 1, .1
obj = Phantom(x=[0], T1=[T1], T2=[T2])
# Define the 90-degree hard excitation pulse parameters
ampRF = sys.B1 # amplitude of the RF pulse
durRF = π / 2 / (2π * γ * ampRF) # duration of the RF pulse
# Define the 90-degree hard excitation pulse parameters
nADC = 16 # number of acquisition samples
durADC = 5 * T2 # duration of the acquisition
# Define the parameters for the last block of the sequence
ampGR = .1 * ampRF # amplitude of the gradient in the last block
durGR = .1 * durRF # duration of the gradient in the last block
# Define sequence struct
matGRs = [Grad(0, durRF) Grad(0, durADC) Grad(ampGR, durGR);
Grad(0, durRF) Grad(0, durADC) Grad(ampGR, durGR);
Grad(0, durRF) Grad(0, durADC) Grad(ampGR, durGR)]
matRFs = [RF(ampRF, durRF) RF(0, durADC) RF(0, durGR)]
vecADCs = [ADC(0, durRF); ADC(nADC, durADC); ADC(0, durGR)]
seq = Sequence(matGRs, matRFs, vecADCs)
# Plot the sequence
p = plot_seq(seq; slider=true, height=300);
savefig(p, "seq.html");
# Perform the simulation and get the output raw signal
sig = simulate(obj, seq, sys)
# Transform the output raw signalt to ismrmrd format (::RawAcquisitionData)
seq.DEF["Nx"], seq.DEF["Ny"], seq.DEF["Nz"] = 1, 1, 1
ismrmrd = rawSignalToISMRMRD([sig;;], seq; phantom=obj, sys=sys)
# Plot the output raw signal
p = plot_signal(ismrmrd; height=300);
savefig(p, "ismrmrd.html");
which gives the following exponential decay output raw signal:
This problem could be resolved by adding the extra argument extrapolation_bc=0
to the function LinearInterpolation()
which is inside the function run_sim_time_iter()
. However, I changed that in a rapid test and the simulation()
threw an error.
It is also weird that in this example the first sample of the acquisition doesn't start at 1, this can be patched in the example by adding a delay in the ADC like so:
delayADC = .001 # a small delay
vecADCs = [ADC(0, durRF); ADC(nADC, durADC, delayADC); ADC(0, durGR)]
, which gives the following expected exponential decay output raw signal that start from ~1:
This issue is also related with the linear interpolation performed to get equally spaced samples, however, apparently, this is not necessary anymore.
Additionally, by using one sample for the acquisition in the previous example:
nADC = 1 # number of acquisition samples
, the function simulation()
throws the following error:
julia> sig = simulate(obj, seq, sys)
ERROR: ArgumentError: range(0.0, stop=0.05, length=1): endpoints differ
Stacktrace:
[1] _linspace1(#unused#::Type{Float64}, start::Float64, stop::Float64, len::Int64)
@ Base .\twiceprecision.jl:726
[2] range_start_stop_length(start::Float64, stop::Float64, len::Int64)
@ Base .\twiceprecision.jl:635
[3] range_start_stop_length
@ .\range.jl:526 [inlined]
[4] _range
@ .\range.jl:128 [inlined]
[5] #range#59
@ .\range.jl:111 [inlined]
[6] get_sample_times(seq::Sequence)
@ KomaMRI C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\datatypes\sequence\ADC.jl:36
[7] get_variable_times(seq::Sequence; dt::Float64, dt_rf::Float64)
@ KomaMRI C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\simulation\TimeStepCalculation.jl:50
[8] #get_uniform_times#171
@ C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\simulation\TimeStepCalculation.jl:36 [inlined]
[9] simulate(obj::Phantom, seq::Sequence, sys::Scanner; simParams::Dict{String, Any}, w::Nothing)
@ KomaMRI C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\simulation\SimulatorCore.jl:211
[10] simulate(obj::Phantom, seq::Sequence, sys::Scanner)
@ KomaMRI C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\simulation\SimulatorCore.jl:206
[11] top-level scope
@ REPL[965]:1
[12] top-level scope
@ C:\Users\beorostica\.julia\packages\CUDA\DfvRa\src\initialization.jl:52