diff --git a/src/all_building_blocks/spoilt_slice_selects.jl b/src/all_building_blocks/spoilt_slice_selects.jl index b4830fd7ea60393ef7599beee461b7508d8a4bbc..6cbcceaff858aa82b70abc37563a1ec1f25599b7 100644 --- a/src/all_building_blocks/spoilt_slice_selects.jl +++ b/src/all_building_blocks/spoilt_slice_selects.jl @@ -4,7 +4,7 @@ import LinearAlgebra: norm import StaticArrays: SVector import JuMP: @constraint, @objective, objective_function import ...BuildSequences: global_model, global_scanner -import ...Variables: VariableType, duration, rise_time, flat_time, effective_time, qval, gradient_strength, slew_rate, inverse_slice_thickness, get_free_variable, get_pulse +import ...Variables: VariableType, duration, rise_time, flat_time, effective_time, qval, gradient_strength, slew_rate, inverse_slice_thickness, get_free_variable, get_pulse, set_simple_constraints! import ...Components: ChangingGradient, ConstantGradient, RFPulseComponent import ..BaseBuildingBlocks: BaseBuildingBlock @@ -35,26 +35,46 @@ struct SpoiltSliceSelect <: BaseBuildingBlock slew_rate :: Number end -function SpoiltSliceSelect(pulse::RFPulseComponent; orientation=[0, 0, 1], group=nothing, spoiler_scale=nothing, kwargs...) +function SpoiltSliceSelect(pulse::RFPulseComponent; orientation=[0, 0, 1], group=nothing, spoiler_scale=nothing, slice_thickness=nothing, kwargs...) model = global_model() - res = SpoiltSliceSelect( - orientation ./ norm(orientation), - get_free_variable(nothing; start=0.1), - get_free_variable(nothing; start=0.1), - get_free_variable(nothing; start=0.05), - pulse, - get_free_variable(nothing; start=0.1), - get_free_variable(nothing; start=0.1), - group, - slew_rate(global_scanner()), - ) - for time_var in (res.rise_time1, res.flat_time1, res.diff_time, res.flat_time2, res.fall_time2) - @constraint model time_var >= 0 + res = nothing + if isinf(slice_thickness) + rise_time_var = get_free_variable(nothing) + flat_time_var = get_free_variable(nothing) + res = SpoiltSliceSelect( + orientation ./ norm(orientation), + rise_time_var, + flat_time_var, + 0., + pulse, + flat_time_var, + rise_time_var, + group, + slew_rate(global_scanner()), + ) + for time_var in (rise_time, flat_time) + @constraint model time_var >= 0 + end + else + res = SpoiltSliceSelect( + orientation ./ norm(orientation), + get_free_variable(nothing; start=0.1), + get_free_variable(nothing; start=0.1), + get_free_variable(nothing; start=0.05), + pulse, + get_free_variable(nothing; start=0.1), + get_free_variable(nothing; start=0.1), + group, + slew_rate(global_scanner()), + ) + for time_var in (res.rise_time1, res.flat_time1, res.diff_time, res.flat_time2, res.fall_time2) + @constraint model time_var >= 0 + end + @constraint model res.diff_time <= res.rise_time1 + @constraint model res.diff_time <= res.fall_time2 + @constraint model qval(res, nothing, 1) == qval(res, 1, nothing) end - @constraint model res.diff_time <= res.rise_time1 - @constraint model res.diff_time <= res.fall_time2 - @constraint model qval(res, nothing, 1) == qval(res, 1, nothing) if !isnothing(spoiler_scale) if spoiler_scale == :min # note that spoiler_scale is inverse of qval @objective model Min objective_function(model) - qval(res, nothing, 1) diff --git a/src/helper_functions.jl b/src/helper_functions.jl index d475012bd6a592d07c8298555734ac50f5f6e53c..cf1a0d6bee08f3e254c84330d5815b32e0f5e112 100644 --- a/src/helper_functions.jl +++ b/src/helper_functions.jl @@ -96,8 +96,9 @@ For an [`InstantPulse`](@ref) (i.e., `shape=:instant`), only the `flip_angle`, ` - `spoiler`: set to the spatial scale on which the spins should be dephased in mm. For rotating spoilers, this does include the contribution from the slice select gradient as well. - `rotate_grad`: name of the parameter with which the slice selection and spoiler gradient will be rotated after sequence optimisation (default: `:FOV`). - `scanner`: overrides the [`global_scanner`](@ref) for this part of the sequence. Recommended to set only if not part of a larger sequence. +- `orientation`: vector with orientation of slice select gradient and pulses (defaults: z-direction). """ -function refocus_pulse(; flip_angle=180, phase=0., frequency=0., shape=:sinc, slice_thickness=Inf, Nzeros=3, group=nothing, rotate_grad=:FOV, bandwidth=nothing, duration=nothing, spoiler=Inf, scanner=nothing, optimise=false) +function refocus_pulse(; flip_angle=180, phase=0., frequency=0., shape=:sinc, slice_thickness=Inf, Nzeros=3, group=nothing, bandwidth=nothing, duration=nothing, spoiler=Inf, scanner=nothing, optimise=false, orientation=[0, 0, 1]) build_sequence(scanner; optimise=optimise) do pulse = _get_pulse(shape, flip_angle, phase, frequency, Nzeros, group, bandwidth, duration) if pulse isa InstantPulse && !isinf(slice_thickness) @@ -107,18 +108,11 @@ function refocus_pulse(; flip_angle=180, phase=0., frequency=0., shape=:sinc, sl if isinf(slice_thickness) return pulse else - return Trapezoid(pulse=pulse, duration=:min, slice_thickness=[Inf, Inf, slice_thickness], orientation=[0, 0, 1.], rotate=rotate_grad) + return SliceSelect(pulse=pulse, duration=:min, slice_thickness=slice_thickness, orientation=orientation, group=:FOV) end else - orientation=isnothing(rotate_grad) ? [1, 1, 1] : [0, 0, 1] - if isinf(slice_thickness) - grad = Trapezoid(orientation=orientation, duration=:min, rotate=rotate_grad) - @constraint global_model() qvec(grad)[3] == 2Ï€ * 1e-3 / spoiler - return Sequence(grad, pulse, grad; TR=Inf) - else - res = SpoiltSliceSelect(pulse; orientation=orientation, duration=:min, rotate=rotate_grad, slice_thickness=slice_thickness, spoiler_scale=spoiler) - return res - end + res = SpoiltSliceSelect(pulse; orientation=orientation, duration=:min, group=:FOV, slice_thickness=slice_thickness, spoiler_scale=spoiler) + return res end end end