Skip to content

Commit 7ceced7

Browse files
author
Arthur Allilaire
committed
fix knot collapse at large absolute time
1 parent eae656a commit 7ceced7

5 files changed

Lines changed: 36 additions & 7 deletions

File tree

KomaMRIBase/src/datatypes/Sequence.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -703,8 +703,8 @@ function get_samples(seq::Sequence, range; events=[:rf, :gr, :adc], freq_in_phas
703703
fill_if_empty(x) = isempty(x.t) && length(range) == length(seq) ? merge(x, (t=[0.0; dur(seq)], A=zeros(eltype(x.A), 2))) : x
704704
# RF
705705
if :rf in events
706-
t_rf = reduce(vcat, [T0[i] .+ times(seq.RF[1,i], :A) for i in range])
707-
t_Δf = reduce(vcat, [T0[i] .+ times(seq.RF[1,i], :Δf) for i in range])
706+
t_rf = reduce(vcat, [_reseparate_closing_knot!(T0[i] .+ times(seq.RF[1,i], :A)) for i in range])
707+
t_Δf = reduce(vcat, [_reseparate_closing_knot!(T0[i] .+ times(seq.RF[1,i], :Δf)) for i in range])
708708
A_rf = reduce(vcat, [ampls(seq.RF[1,i]; freq_in_phase) for i in range])
709709
A_Δf = reduce(vcat, [freqs(seq.RF[1,i]) for i in range])
710710
A_ψ = reduce(vcat, [rf_frame_phase(seq.RF[1,i]) for i in range])
@@ -719,9 +719,9 @@ function get_samples(seq::Sequence, range; events=[:rf, :gr, :adc], freq_in_phas
719719
gx = [_gradient_interpolation_samples(seq.GR[1,i]) for i in range]
720720
gy = [_gradient_interpolation_samples(seq.GR[2,i]) for i in range]
721721
gz = [_gradient_interpolation_samples(seq.GR[3,i]) for i in range]
722-
t_gx = reduce(vcat, [T0[i] .+ gx[j].t for (j, i) in enumerate(range)])
723-
t_gy = reduce(vcat, [T0[i] .+ gy[j].t for (j, i) in enumerate(range)])
724-
t_gz = reduce(vcat, [T0[i] .+ gz[j].t for (j, i) in enumerate(range)])
722+
t_gx = reduce(vcat, [_strictly_increasing_knots!(_reseparate_closing_knot!(T0[i] .+ gx[j].t)) for (j, i) in enumerate(range)])
723+
t_gy = reduce(vcat, [_strictly_increasing_knots!(_reseparate_closing_knot!(T0[i] .+ gy[j].t)) for (j, i) in enumerate(range)])
724+
t_gz = reduce(vcat, [_strictly_increasing_knots!(_reseparate_closing_knot!(T0[i] .+ gz[j].t)) for (j, i) in enumerate(range)])
725725
A_gx = reduce(vcat, [g.A for g in gx])
726726
A_gy = reduce(vcat, [g.A for g in gy])
727727
A_gz = reduce(vcat, [g.A for g in gz])

KomaMRIBase/src/datatypes/sequence/Grad.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,10 @@ _amplitude_roundoff_tol(A) = 1000 * eps(float(maximum(abs, A)))
253253

254254
function _strictly_increasing_knots!(t)
255255
for i in 2:length(t)
256-
t[i] <= t[i - 1] && (t[i] = t[i - 1] + MIN_RISE_TIME)
256+
if t[i] <= t[i - 1]
257+
gap = max(MIN_RISE_TIME, eps(t[i - 1]))
258+
t[i] = t[i - 1] + gap
259+
end
257260
end
258261
return t
259262
end

KomaMRIBase/src/timing/KeyValuesCalculation.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,13 @@ function _separate_closing_knot!(t)
105105
return t
106106
end
107107

108+
# Idempotent re-separation of knots that could collapse after rebasing to absolute time
109+
function _reseparate_closing_knot!(t)
110+
length(t) >= 2 || return t
111+
t[end - 1] = min(t[end - 1], t[end] - max(MIN_RISE_TIME, eps(t[end])))
112+
return t
113+
end
114+
108115
_gradient_times(gr::TrapezoidalGrad) = cumsum([gr.delay; gr.rise; gr.T; gr.fall])
109116

110117
times(gr::TrapezoidalGrad) =

KomaMRIBase/src/timing/TimeStepCalculation.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ function get_variable_times(seq; Δt=1e-3, Δt_rf=1e-5, motion=NoMotion())
114114
if is_Gy_on(s) append!(active_gradients, s.GR.y) end
115115
if is_Gz_on(s) append!(active_gradients, s.GR.z) end
116116
for y = active_gradients
117-
ts = times(y) .+ t0
117+
ts = _reseparate_closing_knot!(times(y) .+ t0)
118118
taux = points_from_key_times([next_time(ts[1]); ts; prev_time(ts[end])]; dt=Δt)
119119
append!(t_block, taux)
120120
end

KomaMRIBase/test/runtests.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,25 @@ using TestItems, TestItemRunner
813813
@test all(diff(seqd.t) .> 0)
814814
end
815815

816+
@testset "closing knot survives FP rebasing at large t0" begin
817+
for t0 in (270.0, 1500.0), event in (RF(1e-5, 1e-3), Grad(1e-3, 1e-3))
818+
ts = KomaMRIBase._reseparate_closing_knot!(t0 .+ KomaMRIBase.times(event))
819+
@test ts[end-1] < ts[end]
820+
end
821+
end
822+
823+
@testset "get_samples and get_variable_times keep knots separated at large t0" begin
824+
T, offset = 1e-3, 300.0
825+
rf_seq = Sequence(); rf_seq += Delay(offset); @addblock rf_seq += RF(1e-5, T)
826+
gr_seq = Sequence(); gr_seq += Delay(offset); @addblock gr_seq += Grad(1e-3, T)
827+
ref_seq = Sequence(); ref_seq += Delay(0.5); @addblock ref_seq += Grad(1e-3, T)
828+
rf_t = KomaMRIBase.get_samples(rf_seq).rf.t
829+
@test rf_t[end-1] < rf_t[end]
830+
@test all(diff(KomaMRIBase.get_samples(gr_seq).gx.t) .> 0)
831+
@test length(KomaMRIBase.get_variable_times(gr_seq)[1]) ==
832+
length(KomaMRIBase.get_variable_times(ref_seq)[1])
833+
end
834+
816835
@testset "SequenceFunctions" begin
817836
seq = PulseDesigner.EPI_example()
818837
t, Δt = KomaMRIBase.get_variable_times(seq; Δt=1)

0 commit comments

Comments
 (0)