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

Create generic building block

parent c9dcbd0f
No related branches found
No related tags found
No related merge requests found
......@@ -15,8 +15,9 @@ Main interface:
- [`qvec`](@ref) returns area under curve for (part of) the gradient waveform.
Sub-types need to implement:
- `Base.keys`: returns sequence of keys to all the components
- `Base.getindex`: returns the actual component for each key
- `Base.keys`: returns sequence of keys to all the components.
- `Base.getindex`: returns the actual component for each key.
- [`duration`](@ref): total duration of the building block.
"""
abstract type BaseBuildingBlock <: ContainerBlock end
......@@ -106,7 +107,7 @@ Gets the sequence of [`GradientWaveform`](@ref) from the event with key `first`
Setting `first` to nothing indicates to start from the beginning of the `building_block`.
Similarly, setting `last` to nothing indicates to continue till the end of the `building_block`.
"""
function waveform_seqeuence(bb::BaseBuildingBlock, first, last)
function waveform_sequence(bb::BaseBuildingBlock, first, last)
started = isnothing(first)
current_grad = current_start = nothing
parts = GradientWaveform[]
......
module BuildingBlocks
import LinearAlgebra: norm
import ..BaseBuildingBlocks: BaseBuildingBlock, events, waveform_sequence
import ...Variables: VariableType, duration, make_generic
import ...Components: BaseComponent, DelayedEvent
import ...Readouts: InstantReadout, ADC
import ...Pulses: RFPulseBlock
import ...Gradients: GradientBlock
import ...BuildingBlocks: scanner_constraints!, make_generic
"""
BuildingBlock(waveform, events; min_duration=nothing, orientation=nothing)
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.
"""
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)
events = Any[events...]
waveform = Any[waveform...]
ndim = isnothing(orientat) ? 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(min_duration)
min_duration = waveform[end][1]
end
if !(min_duration waveform[end][1])
@assert min_duration > waveform[end][1]
push!(waveform, (min_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, NoGradientBlock{ndim}(duration))
elseif norm(prev_grad) norm(grad)
push!(components, ConstantGradientBlock(prev_grad, duration))
else
push!(components, ChangingGradientBlock(prev_grad, (grad .- prev_grad) ./ duration, duration))
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(go::BuildingBlock) = go.duration
end
\ No newline at end of file
module Generic
import ..Abstract: AbstractOverlapping, interruptions, duration, waveform_sequence
import ...Variables: VariableType, duration
import ...Wait: WaitBlock
import ...Readouts: InstantReadout, ADC
import ...Pulses: RFPulseBlock
import ...Gradients: GradientBlock
import ...BuildingBlocks: scanner_constraints!, make_generic
"""
GenericWaveform(duration, waveform, interruptions)
Generic description that can capture any overlapping gradients, RF pulses, and/or readouts.
Interruptions are stores as tuples with 3 fields:
- `index`: which part of the `waveform` is being interrupted (cannot be a free variable).
- `time`: time of the interruption relative to the start of the `waveform` part (between 0 and the length of this part). This can be a variable.
- `block`: [`RFPulseBlock`](@ref) or [`InstantReadout`](@ref)
"""
struct GenericOverlapping <: AbstractOverlapping
duration :: VariableType
waveform :: Vector{Union{WaitBlock, GradientBlock}}
interruptions :: Vector{NamedTuple{(:index, :time, :object), Tuple{Int64, <:VariableType, <:Union{RFPulseBlock, InstantReadout, ADC}}}}
function GenericOverlapping(duration::VariableType, waveform::AbstractVector, interruptions::AbstractVector=[])
res = new(duration, waveform, interruptions)
scanner_constraints!.(waveform)
return res
end
end
GenericOverlapping(other_waveform::AbstractOverlapping) = GenericOverlapping(duration(other_waveform), waveform_sequence(other_waveform), interruptions(other_waveform))
make_generic(ao::AbstractOverlapping) = GenericOverlapping(
duration(ao),
waveform_sequence(ao),
[(index=i.index, time=i.time, object=make_generic(i.object)) for i in interruptions(ao)]
)
waveform_sequence(go::GenericOverlapping) = go.waveform
interruptions(go::GenericOverlapping) = go.interruptions
duration(go::GenericOverlapping) = go.duration
make_generic(wait::WaitBlock) = GenericOverlapping(duration(wait), [], [])
end
\ No newline at end of file
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