From 6ae11cdfd52785c68ddd27d416da4e6b1dbdd6f5 Mon Sep 17 00:00:00 2001 From: Michiel Cottaar <michiel.cottaar@ndcn.ox.ac.uk> Date: Tue, 20 Feb 2024 13:35:58 +0000 Subject: [PATCH] Refactor abstract containers away from concrete containers --- src/MRIBuilder.jl | 24 +--- .../all_building_blocks.jl | 14 --- src/all_building_blocks/building_blocks.jl | 94 --------------- src/all_building_blocks/wait_blocks.jl | 23 ---- src/all_sequences/all_sequences.jl | 13 -- src/all_sequences/sequences.jl | 57 --------- .../abstract.jl} | 4 +- src/{ => containers}/alternatives.jl | 6 +- .../building_blocks.jl} | 114 +++++++++++++++++- src/containers/containers.jl | 12 ++ .../sequences.jl} | 60 ++++++++- src/{all_sequences => parts}/epi_readouts.jl | 4 +- src/{ => parts}/helper_functions.jl | 14 ++- src/parts/parts.jl | 15 +++ .../slice_select_rephases.jl | 4 +- .../spoilt_slice_selects.jl | 2 +- .../trapezoids.jl | 2 +- src/pathways.jl | 5 +- 18 files changed, 220 insertions(+), 247 deletions(-) delete mode 100644 src/all_building_blocks/all_building_blocks.jl delete mode 100644 src/all_building_blocks/building_blocks.jl delete mode 100644 src/all_building_blocks/wait_blocks.jl delete mode 100644 src/all_sequences/all_sequences.jl delete mode 100644 src/all_sequences/sequences.jl rename src/{container_blocks.jl => containers/abstract.jl} (94%) rename src/{ => containers}/alternatives.jl (93%) rename src/{all_building_blocks/base_building_blocks.jl => containers/building_blocks.jl} (61%) create mode 100644 src/containers/containers.jl rename src/{all_sequences/base_sequences.jl => containers/sequences.jl} (56%) rename src/{all_sequences => parts}/epi_readouts.jl (96%) rename src/{ => parts}/helper_functions.jl (96%) create mode 100644 src/parts/parts.jl rename src/{all_sequences => parts}/slice_select_rephases.jl (92%) rename src/{all_building_blocks => parts}/spoilt_slice_selects.jl (99%) rename src/{all_building_blocks => parts}/trapezoids.jl (99%) diff --git a/src/MRIBuilder.jl b/src/MRIBuilder.jl index f26f66f..9352a7e 100644 --- a/src/MRIBuilder.jl +++ b/src/MRIBuilder.jl @@ -7,12 +7,9 @@ include("scanners.jl") include("build_sequences.jl") include("variables.jl") include("components/components.jl") -include("container_blocks.jl") -include("all_building_blocks/all_building_blocks.jl") -include("all_sequences/all_sequences.jl") -include("alternatives.jl") +include("containers/containers.jl") include("pathways.jl") -include("helper_functions.jl") +include("parts/parts.jl") #include("printing.jl") import .BuildSequences: build_sequence, global_model, global_scanner @@ -24,26 +21,17 @@ export Scanner, B0, Siemens_Connectom, Siemens_Prisma, Siemens_Terra import .Variables: variables, duration, effective_time, flip_angle, amplitude, phase, frequency, bandwidth, N_left, N_right, qval, δ, rise_time, flat_time, slew_rate, gradient_strength, qvec, qval_square, slice_thickness, inverse_slice_thickness, fov, inverse_fov, voxel_size, inverse_voxel_size, resolution, nsamples, oversample, dwell_time, ramp_overlap, spoiler_scale export variables, duration, effective_time, flip_angle, amplitude, phase, frequency, bandwidth, N_left, N_right, qval, δ, rise_time, flat_time, slew_rate, gradient_strength, qvec, qval_square, slice_thickness, inversne_slice_thickness, fov, inverse_fov, voxel_size, inverse_voxel_size, resolution, nsamples, oversample, dwell_time, ramp_overlap, spoiler_scale -import .ContainerBlocks: ContainerBlock, start_time, end_time -export start_time, end_time - import .Components: InstantPulse, ConstantPulse, SincPulse, GenericPulse, InstantGradient, SingleReadout, ADC export InstantPulse, ConstantPulse, SincPulse, GenericPulse, InstantGradient, SingleReadout, ADC -import .AllBuildingBlocks: waveform, waveform_sequence, events, BaseBuildingBlock, BuildingBlock, Trapezoid, SliceSelect, LineReadout, SpoiltSliceSelect, Wait -export waveform, waveform_sequence, events, BaseBuildingBlock, BuildingBlock, Trapezoid, SliceSelect, LineReadout, SpoiltSliceSelect, Wait - -import .AllSequences: BaseSequence, nrepeat, Sequence, EPIReadout -export BaseSequence, nrepeat, Sequence, EPIReadout - -import .Alternatives: AlternativeBlocks, match_blocks! -export AlternativeBlocks, match_blocks! +import .Containers: ContainerBlock, start_time, end_time, waveform, waveform_sequence, events, BaseBuildingBlock, BuildingBlock, Wait, BaseSequence, nrepeat, Sequence, AlternativeBlocks, match_blocks!, get_index_single_TR +export ContainerBlock, start_time, end_time, waveform, waveform_sequence, events, BaseBuildingBlock, BuildingBlock, Wait, BaseSequence, nrepeat, Sequence, AlternativeBlocks, match_blocks!, get_index_single_TR import .Pathways: Pathway, duration_transverse, duration_dephase, bval, bmat export Pathway, duration_transverse, duration_dephase, bval, bmat -import .HelperFunctions: excitation_pulse, refocus_pulse, epi_readout, single_line_readout -export excitation_pulse, refocus_pulse, epi_readout, single_line_readout +import .Parts: excitation_pulse, refocus_pulse, epi_readout, single_line_readout, Trapezoid, SliceSelect, LineReadout, opposite_kspace_lines, SpoiltSliceSelect, SliceSelectRephase, EPIReadout +export excitation_pulse, refocus_pulse, epi_readout, single_line_readout, Trapezoid, SliceSelect, LineReadout, opposite_kspace_lines, SpoiltSliceSelect, SliceSelectRephase, EPIReadout import JuMP: @constraint, @objective, objective_function, value, Model export @constraint, @objective, objective_function, value, Model diff --git a/src/all_building_blocks/all_building_blocks.jl b/src/all_building_blocks/all_building_blocks.jl deleted file mode 100644 index e841e50..0000000 --- a/src/all_building_blocks/all_building_blocks.jl +++ /dev/null @@ -1,14 +0,0 @@ -module AllBuildingBlocks -include("base_building_blocks.jl") -include("building_blocks.jl") -include("trapezoids.jl") -include("spoilt_slice_selects.jl") -include("wait_blocks.jl") - -import .BaseBuildingBlocks: waveform, waveform_sequence, events, BaseBuildingBlock -import .BuildingBlocks: BuildingBlock -import .Trapezoids: Trapezoid, SliceSelect, LineReadout, opposite_kspace_lines -import .SpoiltSliceSelects: SpoiltSliceSelect -import .WaitBlocks: Wait - -end \ No newline at end of file diff --git a/src/all_building_blocks/building_blocks.jl b/src/all_building_blocks/building_blocks.jl deleted file mode 100644 index 92ae8e2..0000000 --- a/src/all_building_blocks/building_blocks.jl +++ /dev/null @@ -1,94 +0,0 @@ -module BuildingBlocks -import LinearAlgebra: norm -import ..BaseBuildingBlocks: BaseBuildingBlock, events, waveform_sequence -import ...Variables: VariableType, duration, make_generic, get_pulse, get_readout, scanner_constraints! -import ...Components: BaseComponent, DelayedEvent, RFPulseComponent, ReadoutComponent - -""" - BuildingBlock(waveform, events; duration=nothing, orientation=nothing, group) - -Generic [`BaseBuildingBlock`](@ref) that can capture any overlapping gradients, RF pulses, and/or readouts. -The gradients cannot contain any free variables. - -## Arguments -- `waveform`: Sequence of 2-element tuples with (time, (Gx, Gy, Gz)). If `orientation` is set then the tuple is expected to look like (time, G). This cannot contain any free variables. -- `events`: Sequence of 2-element tuples with (index, pulse/readout). The start time of the pulse/readout at the start of the gradient waveform element with index `index` (use [`DelayedEvent`](@ref) to make this earlier or later). -- `duration`: duration of this `BuildingBlock`. If not set then it will be assumed to be the time of the last element in `waveform`. -- `orientation`: orientation of the gradients in the waveform. If not set, then the full gradient vector should be given explicitly. -- `group`: group of the gradient waveform -""" -struct BuildingBlock <: BaseBuildingBlock - parts :: Vector{<:BaseComponent} - function BuildingBlock(parts::AbstractVector{<:BaseComponent}) - res = new(duration, parts) - for (_, part) in waveform_sequence(parts) - scanner_constraints!(part) - end - return res - end -end - -function BuildingBlock(waveform::AbstractVector, events::AbstractVector; duration=nothing, orientation=nothing, group=nothing) - events = Any[events...] - waveform = Any[waveform...] - ndim = isnothing(orientation) ? 1 : 3 - zero_grad = isnothing(orientation) ? zeros(3) : 0. - if length(waveform) == 0 || waveform[1][1] > 0. - pushfirst!(waveform, (0., zero_grad)) - events = [(i+1, e) for (i, e) in events] - end - - if isnothing(duration) - duration = waveform[end][1] - end - if !(duration ≈ waveform[end][1]) - @assert duration > waveform[end][1] - push!(waveform, (duration, zero_grad)) - end - components = BaseComponent[] - for (index_grad, ((prev_time, prev_grad), (time, grad))) in enumerate(zip(waveform[1:end-1], waveform[2:end])) - duration = time - prev_time - if norm(prev_grad) <= 1e-12 && norm(grad) <= 1e-12 - push!(components, NoGradient{ndim}(duration)) - elseif norm(prev_grad) ≈ norm(grad) - push!(components, ConstantGradient(prev_grad, orientation, duration, group)) - else - push!(components, ChangingGradient(prev_grad, (grad .- prev_grad) ./ duration, orientation, duration, group)) - end - while length(events) > 0 && index_grad == events[1][1] - (_, event) = popfirst!(events) - push!(components, event) - end - end - return components -end - -make_generic(other_block::BaseBuildingBlock) = BuildingBlock(duration(other_block), [other_block...]) -Base.keys(bb::BuildingBlock) = 1:length(bb.parts) -Base.getindex(bb::BuildingBlock, i::Integer) = bb.parts[i] - -duration(bb::BuildingBlock) = sum(duration, waveform_sequence(bb); init=0.) - -function get_pulse(bb::BuildingBlock) - pulses = [p for p in events(bb) if p isa RFPulseComponent] - if length(pulses) == 0 - error("BuildingBlock does not contain any pulses.") - end - if length(pulses) == 1 - return pulses[1] - end - error("BuildingBlock contains more than one pulse. Not sure which one to return.") -end - -function get_readout(bb::BuildingBlock) - readouts = [r for r in events(bb) if r isa ReadoutComponent] - if length(readouts) == 0 - error("BuildingBlock does not contain any readouts.") - end - if length(readouts) == 1 - return readouts[1] - end - error("BuildingBlock contains more than one readout. Not sure which one to return.") -end - -end \ No newline at end of file diff --git a/src/all_building_blocks/wait_blocks.jl b/src/all_building_blocks/wait_blocks.jl deleted file mode 100644 index f55383f..0000000 --- a/src/all_building_blocks/wait_blocks.jl +++ /dev/null @@ -1,23 +0,0 @@ -module WaitBlocks -import JuMP: @constraint -import ...BuildSequences: global_model -import ...Variables: get_free_variable, VariableType, duration -import ...Components: NoGradient -import ..BaseBuildingBlocks: BaseBuildingBlock - -struct Wait <: BaseBuildingBlock - duration :: VariableType - function Wait(var) - res = new(get_free_variable(var)) - if !(res.duration isa Number) - @constraint global_model() res.duration >= 0 - end - return res - end -end - -duration(wb::Wait) = wb.duration -Base.keys(::Wait) = (Val(:empty),) -Base.getindex(wb::Wait, ::Val{:empty}) = NoGradient{1}(wb.duration) - -end \ No newline at end of file diff --git a/src/all_sequences/all_sequences.jl b/src/all_sequences/all_sequences.jl deleted file mode 100644 index 0e56ea7..0000000 --- a/src/all_sequences/all_sequences.jl +++ /dev/null @@ -1,13 +0,0 @@ -module AllSequences -include("base_sequences.jl") -include("sequences.jl") -include("slice_select_rephases.jl") -include("epi_readouts.jl") - -import .BaseSequences: BaseSequence, nrepeat -import .Sequences: Sequence -import .SliceSelectRephases: SliceSelectRephase -import .EPIReadouts: EPIReadout - - -end \ No newline at end of file diff --git a/src/all_sequences/sequences.jl b/src/all_sequences/sequences.jl deleted file mode 100644 index 81de052..0000000 --- a/src/all_sequences/sequences.jl +++ /dev/null @@ -1,57 +0,0 @@ -module Sequences -import StaticArrays: SVector -import JuMP: @constraint -import ...Variables: get_free_variable, TR, VariableType, duration -import ...AllBuildingBlocks: Wait, BuildingBlock -import ...BuildSequences: global_model -import ...Components: EventComponent -import ..BaseSequences: ContainerBlock, BaseSequence, nrepeat, get_index_single_TR - -""" - Sequence(blocks; TR=:min, nrepeat=0) - Sequence(blocks...; TR=:min, nrepeat=0) - -Defines an MRI sequence from a vector of building blocks. - -## Arguments -- [`nrepeat`](@ref): how often the sequence will repeat itself (keep at default of 0 to repeat indefinetely). -- [`TR`](@ref): how long between repeats in ms (defaults to the duration of the sequence). Can be set to `nothing` to be a free variable. -""" -struct Sequence{N} <: BaseSequence{N} - blocks :: SVector{N, <:ContainerBlock} - TR :: VariableType -end - -function Sequence(blocks::AbstractVector; TR=:min) - blocks = to_block.(blocks) - actual_duration = sum(duration, blocks; init=0.) - if TR == :min - TR = actual_duration - end - res = Sequence{length(blocks)}(SVector{length(blocks)}(blocks), get_free_variable(TR)) - if !(res.TR isa Number) || !(duration(res) isa Number) - @constraint global_model() res.TR >= actual_duration - end - return res -end - -Sequence(blocks...; kwargs...) = Sequence([blocks...]; kwargs...) - -get_index_single_TR(s::Sequence, i::Integer) = s.blocks[i] -nrepeat(::Sequence) = 0 - -""" - to_block(block_like) - -Converst object into something that can be included in the sequence: -- :min/:max/number/variable/nothing => [`Wait`](@ref). -- `building_block` or `sequence` => no change. -- RF pulse/readout => will be embedded within a [`BuildingBlock`](@ref). -""" -to_block(cb::ContainerBlock) = cb -to_block(s::Symbol) = to_block(Val(s)) -to_block(s::Union{VariableType, Nothing, Val{:min}, Val{:max}}) = Wait(s) -to_block(ec::EventComponent) = BuildingBlock([], [(0, ec)]; duration=duration(ec)) - - -end \ No newline at end of file diff --git a/src/container_blocks.jl b/src/containers/abstract.jl similarity index 94% rename from src/container_blocks.jl rename to src/containers/abstract.jl index e20828f..669ff1b 100644 --- a/src/container_blocks.jl +++ b/src/containers/abstract.jl @@ -1,5 +1,5 @@ -module ContainerBlocks -import ..Variables: AbstractBlock, duration, effective_time +module Abstract +import ...Variables: AbstractBlock, duration, effective_time """ Parent type for `BuildingBlock` or `BaseSequence`, i.e., any building block that contains other MRI components/blocks. diff --git a/src/alternatives.jl b/src/containers/alternatives.jl similarity index 93% rename from src/alternatives.jl rename to src/containers/alternatives.jl index f92a86e..2b00577 100644 --- a/src/alternatives.jl +++ b/src/containers/alternatives.jl @@ -1,8 +1,8 @@ module Alternatives import JuMP: @constraint -import ..ContainerBlocks: ContainerBlock -import ..BuildSequences: global_model, fixed -import ..Variables: duration, make_generic +import ..Abstract: ContainerBlock +import ...BuildSequences: global_model, fixed +import ...Variables: duration, make_generic """ AlternativeBlocks(name, blocks) diff --git a/src/all_building_blocks/base_building_blocks.jl b/src/containers/building_blocks.jl similarity index 61% rename from src/all_building_blocks/base_building_blocks.jl rename to src/containers/building_blocks.jl index cb8c762..f3aa83d 100644 --- a/src/all_building_blocks/base_building_blocks.jl +++ b/src/containers/building_blocks.jl @@ -1,7 +1,13 @@ -module BaseBuildingBlocks -import ...ContainerBlocks: ContainerBlock -import ...Components: BaseComponent, GradientWaveform, EventComponent, NoGradient, ChangingGradient, ConstantGradient, split_gradient +""" +Defines [`BaseBuildingBlock`](@ref), [`BuildingBlock`](@ref) and [`Wait`](@ref). +""" +module BuildingBlocks +import LinearAlgebra: norm +import JuMP: @constraint +import ..Abstract: ContainerBlock +import ...Components: BaseComponent, GradientWaveform, EventComponent, NoGradient, ChangingGradient, ConstantGradient, split_gradient, DelayedEvent, RFPulseComponent, ReadoutComponent import ...Variables: qval, bmat_gradient, effective_time +import ...Variables: VariableType, duration, make_generic, get_pulse, get_readout, scanner_constraints! """ Basic BuildingBlock, which can consist of a gradient waveforms with any number of RF pulses/readouts overlaid @@ -175,4 +181,106 @@ function bmat_gradient(bb::BaseBuildingBlock, qstart, index1, index2) end bmat_gradient(bb::BaseBuildingBlock, qstart) = bmat_gradient(bb, qstart, nothing, nothing) +""" + BuildingBlock(waveform, events; duration=nothing, orientation=nothing, group) + +Generic [`BaseBuildingBlock`](@ref) that can capture any overlapping gradients, RF pulses, and/or readouts. +The gradients cannot contain any free variables. + +## Arguments +- `waveform`: Sequence of 2-element tuples with (time, (Gx, Gy, Gz)). If `orientation` is set then the tuple is expected to look like (time, G). This cannot contain any free variables. +- `events`: Sequence of 2-element tuples with (index, pulse/readout). The start time of the pulse/readout at the start of the gradient waveform element with index `index` (use [`DelayedEvent`](@ref) to make this earlier or later). +- `duration`: duration of this `BuildingBlock`. If not set then it will be assumed to be the time of the last element in `waveform`. +- `orientation`: orientation of the gradients in the waveform. If not set, then the full gradient vector should be given explicitly. +- `group`: group of the gradient waveform +""" +struct BuildingBlock <: BaseBuildingBlock + parts :: Vector{<:BaseComponent} + function BuildingBlock(parts::AbstractVector{<:BaseComponent}) + res = new(duration, parts) + for (_, part) in waveform_sequence(parts) + scanner_constraints!(part) + end + return res + end +end + +function BuildingBlock(waveform::AbstractVector, events::AbstractVector; duration=nothing, orientation=nothing, group=nothing) + events = Any[events...] + waveform = Any[waveform...] + ndim = isnothing(orientation) ? 1 : 3 + zero_grad = isnothing(orientation) ? zeros(3) : 0. + if length(waveform) == 0 || waveform[1][1] > 0. + pushfirst!(waveform, (0., zero_grad)) + events = [(i+1, e) for (i, e) in events] + end + + if isnothing(duration) + duration = waveform[end][1] + end + if !(duration ≈ waveform[end][1]) + @assert duration > waveform[end][1] + push!(waveform, (duration, zero_grad)) + end + components = BaseComponent[] + for (index_grad, ((prev_time, prev_grad), (time, grad))) in enumerate(zip(waveform[1:end-1], waveform[2:end])) + duration = time - prev_time + if norm(prev_grad) <= 1e-12 && norm(grad) <= 1e-12 + push!(components, NoGradient{ndim}(duration)) + elseif norm(prev_grad) ≈ norm(grad) + push!(components, ConstantGradient(prev_grad, orientation, duration, group)) + else + push!(components, ChangingGradient(prev_grad, (grad .- prev_grad) ./ duration, orientation, duration, group)) + end + while length(events) > 0 && index_grad == events[1][1] + (_, event) = popfirst!(events) + push!(components, event) + end + end + return components +end + +make_generic(other_block::BaseBuildingBlock) = BuildingBlock(duration(other_block), [other_block...]) +Base.keys(bb::BuildingBlock) = 1:length(bb.parts) +Base.getindex(bb::BuildingBlock, i::Integer) = bb.parts[i] + +duration(bb::BuildingBlock) = sum(duration, waveform_sequence(bb); init=0.) + +function get_pulse(bb::BuildingBlock) + pulses = [p for p in events(bb) if p isa RFPulseComponent] + if length(pulses) == 0 + error("BuildingBlock does not contain any pulses.") + end + if length(pulses) == 1 + return pulses[1] + end + error("BuildingBlock contains more than one pulse. Not sure which one to return.") +end + +function get_readout(bb::BuildingBlock) + readouts = [r for r in events(bb) if r isa ReadoutComponent] + if length(readouts) == 0 + error("BuildingBlock does not contain any readouts.") + end + if length(readouts) == 1 + return readouts[1] + end + error("BuildingBlock contains more than one readout. Not sure which one to return.") +end + +struct Wait <: BaseBuildingBlock + duration :: VariableType + function Wait(var) + res = new(get_free_variable(var)) + if !(res.duration isa Number) + @constraint global_model() res.duration >= 0 + end + return res + end +end + +duration(wb::Wait) = wb.duration +Base.keys(::Wait) = (Val(:empty),) +Base.getindex(wb::Wait, ::Val{:empty}) = NoGradient{1}(wb.duration) + end \ No newline at end of file diff --git a/src/containers/containers.jl b/src/containers/containers.jl new file mode 100644 index 0000000..c62f548 --- /dev/null +++ b/src/containers/containers.jl @@ -0,0 +1,12 @@ +module Containers +include("abstract.jl") +include("building_blocks.jl") +include("sequences.jl") +include("alternatives.jl") + +import .Abstract: ContainerBlock, start_time, end_time, effective_time +import .BuildingBlocks: BaseBuildingBlock, BuildingBlock, Wait, waveform, waveform_sequence, events +import .Sequences: BaseSequence, Sequence, nrepeat, get_index_single_TR +import .Alternatives: AlternativeBlocks, match_blocks! + +end \ No newline at end of file diff --git a/src/all_sequences/base_sequences.jl b/src/containers/sequences.jl similarity index 56% rename from src/all_sequences/base_sequences.jl rename to src/containers/sequences.jl index 4c6df22..41df737 100644 --- a/src/all_sequences/base_sequences.jl +++ b/src/containers/sequences.jl @@ -1,7 +1,14 @@ -module BaseSequences -import ...ContainerBlocks: ContainerBlock, start_time -import ...AllBuildingBlocks: BaseBuildingBlock -import ...Variables: TR, duration +""" +Defines [`BaseSequence`](@ref) and [`Sequence`](@ref) +""" +module Sequences +import StaticArrays: SVector +import JuMP: @constraint +import ...Variables: get_free_variable, TR, VariableType, duration +import ...BuildSequences: global_model +import ...Components: EventComponent +import ..Abstract: ContainerBlock, start_time +import ..BuildingBlocks: Wait, BuildingBlock, BaseBuildingBlock """ Super-type of any sequence of non-overlapping building blocks that should be played after each other. @@ -77,5 +84,50 @@ TR(bs::BaseSequence) = duration(bs) duration(bs::BaseSequence{0}) = 0. duration(bs::BaseSequence) = sum(duration.(bs); init=0.) +""" + Sequence(blocks; TR=:min, nrepeat=0) + Sequence(blocks...; TR=:min, nrepeat=0) + +Defines an MRI sequence from a vector of building blocks. + +## Arguments +- [`nrepeat`](@ref): how often the sequence will repeat itself (keep at default of 0 to repeat indefinetely). +- [`TR`](@ref): how long between repeats in ms (defaults to the duration of the sequence). Can be set to `nothing` to be a free variable. +""" +struct Sequence{N} <: BaseSequence{N} + blocks :: SVector{N, <:ContainerBlock} + TR :: VariableType +end + +function Sequence(blocks::AbstractVector; TR=:min) + blocks = to_block.(blocks) + actual_duration = sum(duration, blocks; init=0.) + if TR == :min + TR = actual_duration + end + res = Sequence{length(blocks)}(SVector{length(blocks)}(blocks), get_free_variable(TR)) + if !(res.TR isa Number) || !(duration(res) isa Number) + @constraint global_model() res.TR >= actual_duration + end + return res +end + +Sequence(blocks...; kwargs...) = Sequence([blocks...]; kwargs...) + +get_index_single_TR(s::Sequence, i::Integer) = s.blocks[i] +nrepeat(::Sequence) = 0 + +""" + to_block(block_like) + +Converst object into something that can be included in the sequence: +- :min/:max/number/variable/nothing => [`Wait`](@ref). +- `building_block` or `sequence` => no change. +- RF pulse/readout => will be embedded within a [`BuildingBlock`](@ref). +""" +to_block(cb::ContainerBlock) = cb +to_block(s::Symbol) = to_block(Val(s)) +to_block(s::Union{VariableType, Nothing, Val{:min}, Val{:max}}) = Wait(s) +to_block(ec::EventComponent) = BuildingBlock([], [(0, ec)]; duration=duration(ec)) end diff --git a/src/all_sequences/epi_readouts.jl b/src/parts/epi_readouts.jl similarity index 96% rename from src/all_sequences/epi_readouts.jl rename to src/parts/epi_readouts.jl index 3ea831b..7f1e6b6 100644 --- a/src/all_sequences/epi_readouts.jl +++ b/src/parts/epi_readouts.jl @@ -1,8 +1,8 @@ module EPIReadouts -import ...AllBuildingBlocks: LineReadout, Trapezoid, opposite_kspace_lines +import ...Containers: BaseSequence, get_index_single_TR +import ..Trapezoids: Trapezoid, opposite_kspace_lines, LineReadout import ...Components: ADC import ...Variables: get_free_variable, VariableType, qval, qvec, set_simple_constraints!, resolution, inverse_voxel_size, inverse_fov, resolution, get_readout, apply_simple_constraint! -import ..BaseSequences: BaseSequence, get_index_single_TR """ EPIReadout(resolution; ky_lines=-resolution[2]:resolution[2], recenter=false, group=:FOV, variables...) diff --git a/src/helper_functions.jl b/src/parts/helper_functions.jl similarity index 96% rename from src/helper_functions.jl rename to src/parts/helper_functions.jl index b27221b..105b112 100644 --- a/src/helper_functions.jl +++ b/src/parts/helper_functions.jl @@ -1,11 +1,13 @@ module HelperFunctions import JuMP: @constraint -import ..AllBuildingBlocks: BuildingBlock, Trapezoid, SpoiltSliceSelect, opposite_kspace_lines, SliceSelect -import ..BuildSequences: global_model, build_sequence -import ..AllSequences: Sequence, SliceSelectRephase -import ..Components: SincPulse, ConstantPulse, InstantPulse -import ..Variables: qvec, flat_time, rise_time -import ..Alternatives: AlternativeBlocks, match_blocks! +import ...Containers: AlternativeBlocks, match_blocks!, BuildingBlock +import ..Trapezoids: Trapezoid, opposite_kspace_lines, SliceSelect +import ..SpoiltSliceSelects: SpoiltSliceSelect +import ..SliceSelectRephases: SliceSelectRephase +import ...BuildSequences: global_model, build_sequence +import ...Containers: Sequence +import ...Components: SincPulse, ConstantPulse, InstantPulse +import ...Variables: qvec, flat_time, rise_time function _get_pulse(shape, flip_angle, phase, frequency, Nzeros, group, bandwidth, duration) diff --git a/src/parts/parts.jl b/src/parts/parts.jl new file mode 100644 index 0000000..d4914e4 --- /dev/null +++ b/src/parts/parts.jl @@ -0,0 +1,15 @@ +module Parts +include("trapezoids.jl") +include("slice_select_rephases.jl") +include("spoilt_slice_selects.jl") +include("epi_readouts.jl") +include("helper_functions.jl") + +import .Trapezoids: Trapezoid, SliceSelect, LineReadout, opposite_kspace_lines +import .SpoiltSliceSelects: SpoiltSliceSelect +import .SliceSelectRephases: SliceSelectRephase +import .EPIReadouts: EPIReadout +import .HelperFunctions: excitation_pulse, refocus_pulse, epi_readout, single_line_readout + + +end \ No newline at end of file diff --git a/src/all_sequences/slice_select_rephases.jl b/src/parts/slice_select_rephases.jl similarity index 92% rename from src/all_sequences/slice_select_rephases.jl rename to src/parts/slice_select_rephases.jl index 9486a64..e458073 100644 --- a/src/all_sequences/slice_select_rephases.jl +++ b/src/parts/slice_select_rephases.jl @@ -1,6 +1,6 @@ module SliceSelectRephases -import ..BaseSequences: BaseSequence, get_index_single_TR -import ...AllBuildingBlocks: SliceSelect, Trapezoid +import ...Containers: BaseSequence, get_index_single_TR +import ..Trapezoids: SliceSelect, Trapezoid import ...Variables: get_pulse, qval, apply_simple_constraint! import ...Components: RFPulseComponent diff --git a/src/all_building_blocks/spoilt_slice_selects.jl b/src/parts/spoilt_slice_selects.jl similarity index 99% rename from src/all_building_blocks/spoilt_slice_selects.jl rename to src/parts/spoilt_slice_selects.jl index c07d073..34aefd3 100644 --- a/src/all_building_blocks/spoilt_slice_selects.jl +++ b/src/parts/spoilt_slice_selects.jl @@ -6,7 +6,7 @@ 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, set_simple_constraints!, gradient_orientation import ...Components: ChangingGradient, ConstantGradient, RFPulseComponent -import ..BaseBuildingBlocks: BaseBuildingBlock +import ...Containers: BaseBuildingBlock """ diff --git a/src/all_building_blocks/trapezoids.jl b/src/parts/trapezoids.jl similarity index 99% rename from src/all_building_blocks/trapezoids.jl rename to src/parts/trapezoids.jl index d504533..4a0a64d 100644 --- a/src/all_building_blocks/trapezoids.jl +++ b/src/parts/trapezoids.jl @@ -10,7 +10,7 @@ import ...Variables: qval, rise_time, flat_time, slew_rate, gradient_strength, v 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 ...BuildSequences: global_model import ...Components: ChangingGradient, ConstantGradient, RFPulseComponent, ADC -import ..BaseBuildingBlocks: BaseBuildingBlock +import ...Containers: BaseBuildingBlock """ diff --git a/src/pathways.jl b/src/pathways.jl index 9f081bd..1724950 100644 --- a/src/pathways.jl +++ b/src/pathways.jl @@ -2,11 +2,8 @@ module Pathways import LinearAlgebra: norm, tr import StaticArrays: SVector, SMatrix import ..Components: NoGradient, RFPulseComponent, ReadoutComponent, InstantGradient, GradientWaveform, DelayedEvent -import ..AllSequences: BaseSequence, Sequence -import ..AllBuildingBlocks: BaseBuildingBlock, waveform, events, waveform_sequence +import ..Containers: BaseSequence, Sequence, BaseBuildingBlock, waveform, events, waveform_sequence, start_time, AlternativeBlocks import ..Variables: qvec, qval, bmat_gradient, VariableType, effective_time, duration, TR -import ..ContainerBlocks: start_time -import ..Alternatives: AlternativeBlocks """ -- GitLab