-
Michiel Cottaar authoredMichiel Cottaar authored
building_blocks.jl 3.23 KiB
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