diff --git a/src/MRIBuilder.jl b/src/MRIBuilder.jl index 9352a7e6ee0e753c2710f42c81cf4d97e69c7c5f..259dca6e666c1c3bbd69f1de306fc55c6d626fbc 100644 --- a/src/MRIBuilder.jl +++ b/src/MRIBuilder.jl @@ -18,8 +18,8 @@ export build_sequence, global_model, global_scanner import .Scanners: Scanner, B0, Siemens_Connectom, Siemens_Prisma, Siemens_Terra 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 .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, TR +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, TR import .Components: InstantPulse, ConstantPulse, SincPulse, GenericPulse, InstantGradient, SingleReadout, ADC export InstantPulse, ConstantPulse, SincPulse, GenericPulse, InstantGradient, SingleReadout, ADC diff --git a/src/containers/sequences.jl b/src/containers/sequences.jl index 75b65a87b5068276ccedbda33b97e85f1c82ff62..deb4b181d342fc4597feb39789f6221b8fef595d 100644 --- a/src/containers/sequences.jl +++ b/src/containers/sequences.jl @@ -4,7 +4,7 @@ Defines [`BaseSequence`](@ref) and [`Sequence`](@ref) module Sequences import StaticArrays: SVector import JuMP: @constraint -import ...Variables: get_free_variable, TR, VariableType, duration +import ...Variables: get_free_variable, TR, VariableType, duration, variables, VariableNotAvailable, Variables import ...BuildSequences: global_model import ...Components: EventComponent import ..Abstract: ContainerBlock, start_time @@ -94,29 +94,59 @@ Defines an MRI sequence from a vector of building blocks. - [`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} +struct Sequence{N, S} <: BaseSequence{N} blocks :: SVector{N, Pair{<:Union{Symbol, Nothing}, <:ContainerBlock}} TR :: VariableType end -function Sequence(blocks::AbstractVector; TR=:min) +function Sequence(blocks::AbstractVector; TR=:min, name=:Sequence) blocks = to_block_pair.(blocks) actual_duration = sum(pair -> duration(pair[2]), blocks; init=0.) if TR == :min TR = actual_duration end - res = Sequence{length(blocks)}(SVector{length(blocks)}(blocks), get_free_variable(TR)) + res = Sequence{length(blocks), name}(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 +for fn in keys(variables) + if fn in (:duration, :TR) + continue + end + @eval function Variables.$fn(seq::Sequence) + res = [] + for (name, block) in seq.blocks + if isnothing(name) + continue + end + try + push!(res, name => Variables.$fn(block)) + catch e + if e isa VariableNotAvailable + continue + end + rethrow() + end + end + if length(res) > 0 + return NamedTuple(res) + else + error("None of the building blocks of $(typeof(seq)) define $($fn)") + end + end +end + +Base.show(io::IO, ::Type{<:Sequence{N, S}}) where {N, S} = print(io, S) + Sequence(blocks...; kwargs...) = Sequence([blocks...]; kwargs...) get_index_single_TR(s::Sequence, i::Integer) = s.blocks[i][2] Base.getindex(seq::Sequence, sym::Symbol) = seq[findfirst(p -> p[1] == sym, seq.blocks)] nrepeat(::Sequence) = 0 +TR(s::Sequence) = s.TR to_block_pair(pair::Pair) = pair[1] => to_block(pair[2]) to_block_pair(other) = nothing => to_block(other)