Skip to content
Snippets Groups Projects
Verified Commit e7c6b33b authored by Michiel Cottaar's avatar Michiel Cottaar
Browse files

Split `split_times` from `linearise`

parent 12615788
No related branches found
No related tags found
No related merge requests found
Pipeline #23362 passed
...@@ -26,8 +26,8 @@ export variables, duration, effective_time, flip_angle, amplitude, phase, freque ...@@ -26,8 +26,8 @@ export variables, duration, effective_time, flip_angle, amplitude, phase, freque
import .Components: InstantPulse, ConstantPulse, SincPulse, GenericPulse, InstantGradient, SingleReadout, ADC import .Components: InstantPulse, ConstantPulse, SincPulse, GenericPulse, InstantGradient, SingleReadout, ADC
export InstantPulse, ConstantPulse, SincPulse, GenericPulse, InstantGradient, SingleReadout, ADC export InstantPulse, ConstantPulse, SincPulse, GenericPulse, InstantGradient, SingleReadout, ADC
import .Containers: ContainerBlock, start_time, end_time, waveform, waveform_sequence, events, BaseBuildingBlock, BuildingBlock, Wait, BaseSequence, nrepeat, Sequence, AlternativeBlocks, match_blocks!, get_index_single_TR, readout_times, edge_times, linearise import .Containers: ContainerBlock, start_time, end_time, waveform, waveform_sequence, events, BaseBuildingBlock, BuildingBlock, Wait, BaseSequence, nrepeat, Sequence, AlternativeBlocks, match_blocks!, get_index_single_TR, readout_times, edge_times, SequencePart, split_times, split_timestep, linearise
export ContainerBlock, start_time, end_time, waveform, waveform_sequence, events, BaseBuildingBlock, BuildingBlock, Wait, BaseSequence, nrepeat, Sequence, AlternativeBlocks, match_blocks!, get_index_single_TR, readout_times, edge_times, linearise export ContainerBlock, start_time, end_time, waveform, waveform_sequence, events, BaseBuildingBlock, BuildingBlock, Wait, BaseSequence, nrepeat, Sequence, AlternativeBlocks, match_blocks!, get_index_single_TR, readout_times, edge_times, SequencePart, split_times, split_timestep, linearise
import .Pathways: Pathway, duration_transverse, duration_dephase, bval, bmat, get_pathway import .Pathways: Pathway, duration_transverse, duration_dephase, bval, bmat, get_pathway
export Pathway, duration_transverse, duration_dephase, bval, bmat, get_pathway export Pathway, duration_transverse, duration_dephase, bval, bmat, get_pathway
......
...@@ -9,6 +9,6 @@ import .Abstract: ContainerBlock, start_time, end_time, effective_time, readout_ ...@@ -9,6 +9,6 @@ import .Abstract: ContainerBlock, start_time, end_time, effective_time, readout_
import .BuildingBlocks: BaseBuildingBlock, BuildingBlock, Wait, waveform, waveform_sequence, events, ndim_grad import .BuildingBlocks: BaseBuildingBlock, BuildingBlock, Wait, waveform, waveform_sequence, events, ndim_grad
import .BaseSequences: BaseSequence, Sequence, nrepeat, get_index_single_TR import .BaseSequences: BaseSequence, Sequence, nrepeat, get_index_single_TR
import .Alternatives: AlternativeBlocks, match_blocks! import .Alternatives: AlternativeBlocks, match_blocks!
import .Linearise: linearise import .Linearise: SequencePart, split_times, split_timestep, linearise
end end
\ No newline at end of file
...@@ -2,7 +2,7 @@ module Linearise ...@@ -2,7 +2,7 @@ module Linearise
import StaticArrays: SVector import StaticArrays: SVector
import ...Components: GradientWaveform, split_timestep import ...Components: GradientWaveform, split_timestep
import ...Variables: amplitude, phase, gradient_strength3, duration import ...Variables: amplitude, phase, gradient_strength3, duration
import ..Abstract: edge_times, start_time, end_time import ..Abstract: edge_times, start_time, end_time, ContainerBlock
import ..BaseSequences: BaseSequence, Sequence import ..BaseSequences: BaseSequence, Sequence
import ..BuildingBlocks: BaseBuildingBlock import ..BuildingBlocks: BaseBuildingBlock
...@@ -14,7 +14,13 @@ end ...@@ -14,7 +14,13 @@ end
""" """
Represents a small part of a larger [`Sequence`](@ref), where the gradient, RF amplitude, and RF phase can all be modeled as changing linearly. SequencePart(sequence, time1, time2)
Represents the time between `time1` and `time2` of a larger [`Sequence`](@ref)
The gradient, RF amplitude, and RF phase are all be modeled as changing linearly during this time.
See [`linearise`](@ref) to split a sequence into such linear parts.
""" """
struct SequencePart struct SequencePart
gradient :: LinearPart{SVector{3, Float64}} gradient :: LinearPart{SVector{3, Float64}}
...@@ -24,22 +30,67 @@ struct SequencePart ...@@ -24,22 +30,67 @@ struct SequencePart
duration :: Float64 duration :: Float64
end end
duration(sp::SequencePart) = sp.duration
function SequencePart(sequence::BaseSequence{N}, time1::Number, time2::Number) where {N}
if !(0 <= time1 <= time2 <= duration(sequence))
error("Sequence timings are out of bound")
end
for key in 1:N
if (end_time(sequence, key) > time1)
return SequencePart(sequence[key], time1 - start_time(sequence, key), time2 - start_time(sequence, key))
end
end
end
function SequencePart(bb::BaseBuildingBlock, time1::Number, time2::Number)
if !(0 <= time1 <= time2 <= duration(bb))
error("Sequence timings are out of bound")
end
tmean = (time1 + time2) / 2
function linearise(seq::BaseSequence; kwargs...) function fit_linear_part(fn)
parts = SequencePart[] start = fn(bb, time1)
for block in seq final = fn(bb, time2)
append!(parts, linearise(block; kwargs...)) middle = fn(bb, tmean)
mid_deviation = @. middle - (start + final) / 2
return LinearPart(start .+ mid_deviation ./ 3, final .+ mid_deviation ./ 3)
end end
return parts
return SequencePart(
fit_linear_part(gradient_strength3),
zero(SVector{3, Float64}),
fit_linear_part(amplitude),
fit_linear_part(phase),
time2 - time1
)
end end
function linearise(bb::BaseBuildingBlock; precision=0.01) duration(sp::SequencePart) = sp.duration
parts = SequencePart[]
if duration(bb) <= 0. """
return parts split_times(sequence; precision=0.01)
Suggests at what times to split a sequence into linear parts (see [`linearise`](@ref)).
The split times will include any time when:
- the starting/end points of building blocks, gradients, RF pulses, or ADC readouts.
- a gradient or RF pulse is discontinuous in the first derivative
- the time of any instantaneous gradients, RF pulses, or readouts.
Continuous gradient waveforms or RF pulses might be split up further to ensure the linear approximations meet the required `precision` (see [`split_timestep`](@ref)).
"""
function split_times(sequence::BaseSequence; precision=0.01)
splits = Float64[]
for block in sequence
append!(splits, split_times(block; precision=precision))
end end
return sort(unique(splits))
end
function split_times(bb::BaseBuildingBlock; precision=0.01)
splits = Float64[]
edges = edge_times(bb) edges = edge_times(bb)
for (t1, t2) in zip(edges[1:end-1], edges[2:end]) for (t1, t2) in zip(edges[1:end-1], edges[2:end])
tmean = (t1 + t2) / 2 tmean = (t1 + t2) / 2
...@@ -50,36 +101,28 @@ function linearise(bb::BaseBuildingBlock; precision=0.01) ...@@ -50,36 +101,28 @@ function linearise(bb::BaseBuildingBlock; precision=0.01)
if !(start_time(bb, key) < tmean < end_time(bb, key)) if !(start_time(bb, key) < tmean < end_time(bb, key))
continue continue
end end
new_timestep = split_timestep(block, precision) new_timestep = split_timestep(bb[key], precision)
if new_timestep < timestep if new_timestep < timestep
timestep = new_timestep timestep = new_timestep
end end
end end
nsteps = div(t2 - t1, timestep, RoundUp) nsteps = (isinf(timestep) ? 1 : Int(div(t2 - t1, timestep, RoundUp))) + 1
tsplits = range(t1, t2, length=nsteps) append!(splits, range(t1, t2, length=nsteps))
for (t1b, t2b) in zip(tsplits[1:end-1], tsplits[2:end])
tmeanb = (t1b + t2b) / 2
function fit_linear_part(fn)
start = fn(bb, t1b)
final = fn(bb, t2b)
middle = fn(bb, tmeanb)
mid_deviation = @. middle - (start + final) / 2
return LinearPart(start .+ mid_deviation ./ 3, final .+ mid_deviation ./ 3)
end
push!(parts, SequencePart(
fit_linear_part(gradient_strength3),
zero(SVector{3, Float64}),
fit_linear_part(amplitude),
fit_linear_part(phase),
t2b - t1b
))
end
end end
return parts return sort(unique(splits))
end end
"""
linearise(sequence[, times])
Splits any [`BaseSequence`](@ref) or [`BaseBuildingBlock`](@ref) into a series of [`SequencePart`](@ref) objects where the gradients/pulses are approximated to be linear.
If the `times` are not explicitly set they will be obtained from [`split_times`](@ref).
"""
linearise(container::ContainerBlock) = linearise(container, split_times(container))
linearise(container::ContainerBlock, times::AbstractVector{<:Number}) = [SequencePart(container, t1, t2) for (t1, t2) in zip(times[1:end-1], times[2:end])]
end end
\ No newline at end of file
...@@ -311,10 +311,6 @@ function walk_pathway!(block::BaseBuildingBlock, walker::PathwayWalker, pulse_ef ...@@ -311,10 +311,6 @@ function walk_pathway!(block::BaseBuildingBlock, walker::PathwayWalker, pulse_ef
current_time = block_start_time current_time = block_start_time
for (index_inter, interruption) in events(block) for (index_inter, interruption) in events(block)
if interruption isa Tuple
interruption = interruption[2]
end
# determine if action should be taken # determine if action should be taken
if interruption isa RFPulseComponent if interruption isa RFPulseComponent
if iszero(length(pulse_effects)) if iszero(length(pulse_effects))
......
...@@ -13,6 +13,7 @@ In addition this defines: ...@@ -13,6 +13,7 @@ In addition this defines:
""" """
module Variables module Variables
import JuMP: @constraint, @variable, Model, @objective, objective_function, AbstractJuMPScalar import JuMP: @constraint, @variable, Model, @objective, objective_function, AbstractJuMPScalar
import StaticArrays: SVector
import ..Scanners: gradient_strength, slew_rate, Scanner import ..Scanners: gradient_strength, slew_rate, Scanner
import ..BuildSequences: global_model, global_scanner, fixed import ..BuildSequences: global_model, global_scanner, fixed
...@@ -290,7 +291,7 @@ for base_fn in [:qval, :gradient_strength, :slew_rate] ...@@ -290,7 +291,7 @@ for base_fn in [:qval, :gradient_strength, :slew_rate]
@eval function $fn3(bb::AbstractBlock, args...; kwargs...) @eval function $fn3(bb::AbstractBlock, args...; kwargs...)
value = $base_fn(bb, args...; kwargs...) value = $base_fn(bb, args...; kwargs...)
if value isa Number && iszero(value) if value isa Number && iszero(value)
return value return zero(SVector{3, Float64})
elseif value isa AbstractVector elseif value isa AbstractVector
return value return value
else else
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment