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

Switch from split_times to single split timestep

parent 731cdbf2
No related branches found
No related tags found
No related merge requests found
Pipeline #23353 canceled
...@@ -37,10 +37,16 @@ abstract type ReadoutComponent <: EventComponent end ...@@ -37,10 +37,16 @@ abstract type ReadoutComponent <: EventComponent end
""" """
split_times(component, t1, t2, precision) split_timestep(component, precision)
Indicates at what timepoints a given [`BaseComponent`](@ref) should be split during linearisation to achieve the given precision. Indicates the maximum timestep that a component can be linearised with and still achieve the required `precision`.
Typically, this will be determined by the maximum second derivative:
``\\sqrt{\\frac{2 \\epsilon}{max(|d^2y/dx^2|)}}``
It should be infinite if the component is linear.
""" """
function split_times end function split_timestep end
end end
\ No newline at end of file
...@@ -5,7 +5,7 @@ include("instant_gradients.jl") ...@@ -5,7 +5,7 @@ include("instant_gradients.jl")
include("pulses/pulses.jl") include("pulses/pulses.jl")
include("readouts/readouts.jl") include("readouts/readouts.jl")
import .AbstractTypes: BaseComponent, GradientWaveform, EventComponent, RFPulseComponent, ReadoutComponent, split_times import .AbstractTypes: BaseComponent, GradientWaveform, EventComponent, RFPulseComponent, ReadoutComponent, split_timestep
import .GradientWaveforms: ConstantGradient, ChangingGradient, NoGradient, split_gradient import .GradientWaveforms: ConstantGradient, ChangingGradient, NoGradient, split_gradient
import .InstantGradients: InstantGradient import .InstantGradients: InstantGradient
import .Pulses: GenericPulse, InstantPulse, SincPulse, ConstantPulse import .Pulses: GenericPulse, InstantPulse, SincPulse, ConstantPulse
......
...@@ -21,11 +21,11 @@ include("constant_gradient_blocks.jl") ...@@ -21,11 +21,11 @@ include("constant_gradient_blocks.jl")
include("no_gradient_blocks.jl") include("no_gradient_blocks.jl")
import ..AbstractTypes: GradientWaveform, split_times import ..AbstractTypes: GradientWaveform, split_timestep
import .NoGradientBlocks: NoGradient import .NoGradientBlocks: NoGradient
import .ChangingGradientBlocks: ChangingGradient, split_gradient import .ChangingGradientBlocks: ChangingGradient, split_gradient
import .ConstantGradientBlocks: ConstantGradient import .ConstantGradientBlocks: ConstantGradient
split_times(wv::GradientWaveform, t1, t2, precision) = [t1, t2] split_timestep(wv::GradientWaveform, precision) = Inf
end end
\ No newline at end of file
module ConstantPulses module ConstantPulses
import JuMP: @constraint import JuMP: @constraint
import ...AbstractTypes: RFPulseComponent, split_times import ...AbstractTypes: RFPulseComponent, split_timestep
import ....BuildSequences: global_model import ....BuildSequences: global_model
import ....Variables: duration, amplitude, effective_time, flip_angle, phase, inverse_bandwidth, VariableType, set_simple_constraints!, frequency, make_generic, get_free_variable import ....Variables: duration, amplitude, effective_time, flip_angle, phase, inverse_bandwidth, VariableType, set_simple_constraints!, frequency, make_generic, get_free_variable
import ..GenericPulses: GenericPulse import ..GenericPulses: GenericPulse
...@@ -61,7 +61,7 @@ function make_generic(block::ConstantPulse) ...@@ -61,7 +61,7 @@ function make_generic(block::ConstantPulse)
end end
split_times(pulse::ConstantPulse, t1, t2, precision) = [t1, t2] split_timestep(pulse::ConstantPulse, precision) = Inf
end end
\ No newline at end of file
module GenericPulses module GenericPulses
import ...AbstractTypes: RFPulseComponent, split_times import Polynomials: fit
import ...AbstractTypes: RFPulseComponent, split_timestep
import ....Variables: duration, amplitude, effective_time, flip_angle, make_generic, phase import ....Variables: duration, amplitude, effective_time, flip_angle, make_generic, phase
...@@ -78,38 +79,26 @@ end ...@@ -78,38 +79,26 @@ end
make_generic(gp::GenericPulse) = gp make_generic(gp::GenericPulse) = gp
function split_times(gp::GenericPulse, t1, t2, precision) function split_timestep(gp::GenericPulse, precision)
real_amplitude_precision = precision * amplitude(gp) function second_derivative(arr)
real_phase_precision = 180 * precision max_second_der = 0.
for index in 2:length(arr)-1
current_index = find(t -> t >= t1, gp.time) poly = fit(gp.times[index-1:index+1], arr[index-1:index+1])
final_index = find(t -> t > t2, gp.time) second_der = abs(poly.coeffs[end])
if isnothing(final_index) if second_der > max_second_der
final_index = length(gp.time) max_second_der = second_der
end
times = [t1]
while gp.time[current_index] < t2
current_time = gp.time[current_index]
current_amplitude = gp.amplitude[current_index]
current_phase = gp.phase(current_index)
slope_amplitude = (gp.amplitude[current_index + 1] - current_amplitude) / (gp.time[current_index + 1] - current_time)
slope_phase = (gp.phase[current_index + 1] - current_phase) / (gp.time[current_index + 1] - current_time)
next_index = current_index
for next_index in current_index+2:final_index
if (
abs(slope_amplitude * (gp.time[next_index] - current_time) - gp.amplitude[next_index]) > real_amplitude_precision ||
abs(slope_phase * (gp.time[next_index] - current_time) - gp.phase[next_index]) > real_phase_precision
)
next_index -= 1
push!(times, gp.time[next_index])
break
end end
end end
current_index = next_index return max_second_der
end
max_second_der = max(
second_derivative(gp.amplitude ./ maximum(gp.amplitude)),
second_derivative(gp.phase ./ 360),
)
if iszero(max_second_der)
return Inf
end end
push!(times, t2) return sqrt(2 * precision / max_second_der)
return times
end end
......
...@@ -3,7 +3,7 @@ import JuMP: @constraint ...@@ -3,7 +3,7 @@ import JuMP: @constraint
import QuadGK: quadgk import QuadGK: quadgk
import ....BuildSequences: global_model import ....BuildSequences: global_model
import ....Variables: duration, amplitude, effective_time, flip_angle, phase, inverse_bandwidth, VariableType, set_simple_constraints!, frequency, make_generic, get_free_variable import ....Variables: duration, amplitude, effective_time, flip_angle, phase, inverse_bandwidth, VariableType, set_simple_constraints!, frequency, make_generic, get_free_variable
import ...AbstractTypes: RFPulseComponent, split_times import ...AbstractTypes: RFPulseComponent, split_timestep
import ..GenericPulses: GenericPulse import ..GenericPulses: GenericPulse
""" """
...@@ -101,11 +101,9 @@ function make_generic(block::SincPulse) ...@@ -101,11 +101,9 @@ function make_generic(block::SincPulse)
return GenericPulse(times, amplitudes, phases, effective_time(block)) return GenericPulse(times, amplitudes, phases, effective_time(block))
end end
function split_times(block::SincPulse, t1, t2, precision) function split_timestep(block::SincPulse, precision)
max_second_derivative = π^2/3 * (amplitude(block) / lobe_duration(block))^2 max_second_derivative = π^2/3 * (amplitude(block) / lobe_duration(block))^2
step_size = sqrt(2 * precision / max_second_derivative) return sqrt(2 * precision / max_second_derivative)
npoints = Int(div(t2 - t1, step_size, RoundUp))
return range(t1, t2, length=npoints)
end end
end end
\ No newline at end of file
...@@ -2,9 +2,9 @@ module Readouts ...@@ -2,9 +2,9 @@ module Readouts
include("ADCs.jl") include("ADCs.jl")
include("single_readouts.jl") include("single_readouts.jl")
import ..AbstractTypes: ReadoutComponent, split_times import ..AbstractTypes: ReadoutComponent, split_timestep
import .ADCs: ADC, readout_times import .ADCs: ADC, readout_times
import .SingleReadouts: SingleReadout import .SingleReadouts: SingleReadout
split_times(rc::ReadoutComponent, t1, t2, precision) = [t1, t2] split_times(rc::ReadoutComponent, precision) = Inf
end end
\ No newline at end of file
module Linearise module Linearise
import StaticArrays: SVector import StaticArrays: SVector
import ...Components: GradientWaveform, split_times 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
import ..BaseSequences: BaseSequence, Sequence import ..BaseSequences: BaseSequence, Sequence
...@@ -45,15 +45,18 @@ function linearise(bb::BaseBuildingBlock; precision=0.01) ...@@ -45,15 +45,18 @@ function linearise(bb::BaseBuildingBlock; precision=0.01)
tmean = (t1 + t2) / 2 tmean = (t1 + t2) / 2
# determine where to split this domain # determine where to split this domain
possible_splits = Any[[t1, t2], ] timestep = Inf
for key in keys(bb) for key in keys(bb)
if !(start_time(bb, key) < tmean < end_time(bb, key)) if !(start_time(bb, key) < tmean < end_time(bb, key))
continue continue
end end
block = bb[key] new_timestep = split_timestep(block, precision)
append!(possible_splits, start_time(bb, key) .+ split_times(block, t1 - start_time(bb, key), t2 - start_time(bb, key), precision)) if new_timestep < timestep
timestep = new_timestep
end
end end
tsplits = argmax(length, possible_splits) nsteps = div(t2 - t1, timestep, RoundUp)
tsplits = range(t1, t2, length=nsteps)
for (t1b, t2b) in zip(tsplits[1:end-1], tsplits[2:end]) for (t1b, t2b) in zip(tsplits[1:end-1], tsplits[2:end])
tmeanb = (t1b + t2b) / 2 tmeanb = (t1b + t2b) / 2
......
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