diff --git a/src/components/abstract_types.jl b/src/components/abstract_types.jl index bce65c9dde95c6afcb23a56ec69e3d7af1a3757e..b366b5c9326b0a61a6f38deec698bd5a45881173 100644 --- a/src/components/abstract_types.jl +++ b/src/components/abstract_types.jl @@ -1,5 +1,5 @@ module AbstractTypes -import ...Variables: AbstractBlock, duration, variables, effective_time +import ...Variables: AbstractBlock, duration, variables, effective_time, adjustable """ Super-type for all individual components that form an MRI sequence (i.e., RF pulse, gradient waveform, or readout event). @@ -49,4 +49,8 @@ It should be infinite if the component is linear. """ split_timestep(comp_tuple::Tuple{<:Number, <:EventComponent}, precision::Number) = split_timestep(comp_tuple[2], precision) + +adjustable(::RFPulseComponent) = :pulse +adjustable(::GradientWaveform) = :gradient + end \ No newline at end of file diff --git a/src/components/gradient_waveforms/no_gradient_blocks.jl b/src/components/gradient_waveforms/no_gradient_blocks.jl index 31df5082f8294515a6c13e3f5baccb22f068fe83..0a2465de541a760e73d014c7a2417c66ec89982d 100644 --- a/src/components/gradient_waveforms/no_gradient_blocks.jl +++ b/src/components/gradient_waveforms/no_gradient_blocks.jl @@ -1,6 +1,6 @@ module NoGradientBlocks import StaticArrays: SVector, SMatrix -import ....Variables: VariableType, duration, qval, bmat_gradient, gradient_strength, slew_rate, get_free_variable +import ....Variables: VariableType, duration, qval, bmat_gradient, gradient_strength, slew_rate, get_free_variable, adjustable import ...AbstractTypes: GradientWaveform import ..ChangingGradientBlocks: split_gradient @@ -43,4 +43,6 @@ function split_gradient(ngb::NoGradient, times::VariableType...) return [NoGradient(d) for d in durations] end +adjustable(::NoGradient) = :false + end \ No newline at end of file diff --git a/src/parts/trapezoids.jl b/src/parts/trapezoids.jl index 469f1ba17b3477cd0d03080d75195c2de748145f..0d48a4640bb4f89206852a8779c0b53c74eca0ca 100644 --- a/src/parts/trapezoids.jl +++ b/src/parts/trapezoids.jl @@ -7,7 +7,7 @@ import JuMP: @constraint import StaticArrays: SVector import LinearAlgebra: norm import ...Variables: qval, rise_time, flat_time, slew_rate, gradient_strength, variables, duration, δ, get_free_variable, VariableType, inverse_bandwidth, effective_time, qval_square, duration, set_simple_constraints!, scanner_constraints!, inverse_slice_thickness -import ...Variables: Variables, all_variables_symbols, dwell_time, inverse_fov, inverse_voxel_size, fov, voxel_size, get_gradient, get_pulse, get_readout, gradient_orientation, ramp_overlap +import ...Variables: Variables, all_variables_symbols, dwell_time, inverse_fov, inverse_voxel_size, fov, voxel_size, get_gradient, get_pulse, get_readout, gradient_orientation, ramp_overlap, adjustable, adjust_internal import ...BuildSequences: global_model import ...Components: ChangingGradient, ConstantGradient, RFPulseComponent, ADC import ...Containers: BaseBuildingBlock @@ -116,6 +116,36 @@ duration(g::BaseTrapezoid) = 2 * rise_time(g) + flat_time(g) qval(g::BaseTrapezoid, ::Nothing, ::Nothing) = δ(g) .* gradient_strength(g) .* 2π +adjustable(::BaseTrapezoid) = :gradient + +function adjust_internal(trap::Trapezoid1D; orientation=nothing, scale=1., rotation=nothing) + if !isnothing(orientation) && !isnothing(rotation) + error("Cannot set both the gradient orientation and rotation.") + end + new_orientation = isnothing(orientation) ? (isnothing(rotation) ? trap.orientation : rotation * trap.orientation) : orientation + return Trapezoid1D( + trap.rise_time, + trap.flat_time, + trap.slew_rate * scale, + new_orientation, + trap.group + ) +end + +function adjust_internal(trap::Trapezoid3D; scale=1., rotation=nothing) + return Trapezoid3D( + trap.rise_time, + trap.flat_time, + ( + isnothing(rotation) ? + (trap.slew_rate .* scale) : + (rotation * (trap.slew_rate .* scale)) + ), + trap.group + ) +end + + """ SliceSelect(pulse; orientation=nothing, group=nothing, variables...) diff --git a/src/post_hoc.jl b/src/post_hoc.jl index aaca892ead8fcd7f3d7b2616202908022781aa4a..7b7614f2d9ecc2f724506df36771f3fb6859ffcd 100644 --- a/src/post_hoc.jl +++ b/src/post_hoc.jl @@ -3,7 +3,7 @@ Define post-fitting adjustments of the sequences """ module PostHoc -import ..Variables: AbstractBlock, adjust_internal +import ..Variables: AbstractBlock, adjust_internal, adjustable import ..Components: GradientWaveform, RFPulseComponent, BaseComponent, NoGradient import ..Containers: ContainerBlock @@ -40,12 +40,26 @@ function adjust(block::AbstractBlock; kwargs...) res end -function adjust_helper(container::ContainerBlock, used_names::Set{Symbol}; kwargs...) +function adjust_helper(block::AbstractBlock, used_names::Set{Symbol}; gradient=(), pulse=(), kwargs...) params = [] - for prop_name in propertynames(container) - push!(params, adjust_helper(getproperty(container, prop_name), used_names; kwargs...)) + adjust_type = adjustable(block) + if adjust_type == :false + for prop_name in propertynames(block) + push!(params, adjust_helper(getproperty(block, prop_name), used_names; gradient=gradient, pulse=pulse, kwargs...)) + end + return typeof(block)(params...) + else + if !isnothing(block.group) && (block.group in keys(kwargs)) + push!(used_names, block.group) + return adjust_internal(block; kwargs[block.group]...) + elseif adjust_type == :gradient + push!(used_names, :gradient) + return adjust_internal(block; gradient...) + elseif adjust_type == :pulse + push!(used_names, :pulse) + return adjust_internal(block; pulse...) + end end - return typeof(container)(params...) end adjust_helper(some_value, used_names::Set{Symbol}; kwargs...) = some_value @@ -54,29 +68,4 @@ adjust_helper(dict_variable::AbstractDict, used_names::Set{Symbol}; kwargs...) = adjust_helper(tuple_variable::Tuple, used_names::Set{Symbol}; kwargs...) = map(tuple_variable) do v adjust_helper(v, used_names; kwargs...) end adjust_helper(pair:: Pair, used_names::Set{Symbol}; kwargs...) = adjust_helper(pair[1], used_names; kwargs...) => adjust_helper(pair[2], used_names; kwargs...) -adjust_helper(block::NoGradient, used_names::Set{Symbol}; kwargs...) = block - -function adjust_helper(block::GradientWaveform, used_names::Set{Symbol}; gradient=(), kwargs...) - if !isnothing(block.group) && (block.group in keys(kwargs)) - push!(used_names, block.group) - return adjust_internal(block; kwargs[block.group]...) - else - push!(used_names, :gradient) - return adjust_internal(block; gradient...) - end - return new_block -end - -function adjust_helper(block::RFPulseComponent, used_names::Set{Symbol}; pulse=(), kwargs...) - if !isnothing(block.group) && (block.group in keys(kwargs)) - push!(used_names, block.group) - return adjust_internal(block; kwargs[block.group]...) - else - push!(used_names, :pulse) - return adjust_internal(block; pulse...) - end -end - -adjust_helper(block::BaseComponent) = block - end \ No newline at end of file diff --git a/src/variables.jl b/src/variables.jl index b93eb39110a88f7e7541a54ead9f754a8f01c91f..8754f10fed1c07da674fde38337bd82c10c1193c 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -40,6 +40,18 @@ This is a helper function used by [`adjust`](@ref). """ function adjust_internal end +""" + adjustable(block) + +Returns whether a sequence, building block, or component can be adjusted + +Can return one of: +- `:false`: not adjustable +- `:gradient`: expects gradient adjustment parameters +- `:pulse`: expects RF pulse adjustment parameters +""" +adjustable(::AbstractBlock) = :false + all_variables_symbols = [