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

Define fixed for gradients

parent 85aa6e37
No related branches found
No related tags found
No related merge requests found
......@@ -8,8 +8,8 @@ import ..Variables: variables, start_time, duration, end_time, gradient_strength
Parent type for all individual components out of which a sequence can be built.
Required methods:
- [`duration`](@ref)(block, parameters): returns block duration in ms.
- `to_concrete_block`(sequence, block): converts the block into a `ConcreteBlock`, which will be part of given `Sequence`.
- [`duration`](@ref)(block, parameters): Return block duration in ms.
- [`fixed`](block): Return an equivalent fixed BuildingBlock (i.e., `FixedBlock`, `FixedPulse`, `FixedGradient`, `FixedInstantPulse`, `FixedInstantGradient`, or `InstantReadout`). These all have in common that they have no free variables and explicitly set any gradient and RF pulse profiles.
- [`variables`](@ref): A list of all functions that are used to compute variables of the building block. Any of these can be used in constraints or objective functions.
"""
abstract type BuildingBlock end
......@@ -68,6 +68,17 @@ Function used internally to convert a wide variety of objects into [`BuildingBlo
to_block(bb::BuildingBlock) = bb
"""
fixed(block::BuildingBlock)
Return an equivalent fixed BuildingBlock.
Possible return types are `FixedBlock`, `FixedPulse`, `FixedGradient`, `FixedInstantPulse`, `FixedInstantGradient`, or `InstantReadout`.
These all have in common that they have no free variables and explicitly set any gradient and RF pulse profiles.
"""
function fixed end
"""
scanner_constraints!([model, ]building_block, scanner)
......
module ConcreteBlocks
import JuMP: has_values, optimize!, value
import ..Variables: variables, duration
import ..BuildingBlocks: BuildingBlock
struct ConcreteRFPulse
time :: Vector{Number}
amplitude :: Vector{Number}
phase :: Vector{Number}
end
function ConcreteRFPulse(arr::Vector)
@assert all(length.(arr) .== 3)
ConcreteRFPulse(
[a[1] for a in arr],
[a[2] for a in arr],
[a[3] for a in arr],
)
end
ConcreteRFPulse(::Nothing) = nothing
ConcreteRFPulse(values::Tuple{<:AbstractVector, <:AbstractVector, <:AbstractVector}) = ConcreteRFPulse(values...)
struct ConcreteGradient
time :: Vector{Number}
Gx :: Vector{Number}
Gy :: Vector{Number}
Gz :: Vector{Number}
end
function ConcreteGradient(arr::Vector)
if length(arr[1]) == 4
@assert all(length.(arr) .== 4)
ConcreteGradient(
[a[1] for a in arr],
[a[2] for a in arr],
[a[3] for a in arr],
[a[4] for a in arr],
)
elseif length(arr[1]) == 2
@assert all(length.(arr) .== 2)
ConcreteGradient(
[a[1] for a in arr],
[a[2] for a in arr],
)
else
error()
end
end
function ConcreteGradient(times, gradients)
ConcreteGradient(
times,
[g[1] for g in gradients],
[g[2] for g in gradients],
[g[3] for g in gradients],
)
end
ConcreteGradient(::Nothing) = nothing
ConcreteGradient(values::Tuple{<:Vector, <:Vector}) = ConcreteGradient(values...)
ConcreteGradient(values::Tuple{<:Vector, <:Vector, <:Vector, <:Vector}) = ConcreteGradient(values...)
abstract type AbstractConcreteBlock <: BuildingBlock end
"""
ConcreteBlock(duration; pulse=nothing, gradient=nothing, rotate_bvec=false, readout_times=nothing)
A [`BuildingBlock`](@ref) that is fully defined (i.e., there are no variables to be optimised).
"""
struct ConcreteBlock <: AbstractConcreteBlock
duration :: Float64
pulse :: Union{ConcreteRFPulse, Nothing}
gradient :: Union{ConcreteGradient, Nothing}
readout_times :: Vector{Float64}
rotate_gradient :: Bool
end
function ConcreteBlock(duration::Number; pulse=nothing, gradient=nothing, readout_times=Number[], rotate_gradient=false)
ConcreteBlock(duration, ConcreteRFPulse(pulse), ConcreteGradient(gradient), Float64.(readout_times), rotate_gradient)
end
has_values(c::AbstractConcreteBlock) = true
duration(c::AbstractConcreteBlock) = 0.
duration(c::ConcreteBlock) = c.duration
"""
ConcreteBlock(other_building_block)
Creates a [`ConcreteBlock`](@ref) from another [`BuildingBlock`](@ref).
This will raise an error if the other [`BuildingBlock`](@ref) has not been optimised yet.
If it has been optimised, then [`to_concrete_block`](@ref) will be called.
"""
function ConcreteBlock(block::BuildingBlock)
if !has_values(block)
error("Making `BuildingBlock` objects concrete is only possible after optimisation.")
end
return to_concrete_block(block)
end
"""
to_concrete_block(other_building_block)
Internal function used to create [`ConcreteBlock`](@ref) from any [`BuildingBlock`](@ref).
This needs to be defined for every [`BuildingBlock`](@ref)
"""
function to_concrete_block(cb::ConcreteBlock)
return ConcreteBlock(cb.duration, cb.pulse, cb.gradient, cb.readout_times, cb.rotate_gradient, value.(cb.start_time))
end
variables(::Type{<:AbstractConcreteBlock}) = []
end
\ No newline at end of file
module FixedGradients
import ...BuildingBlock: GradientBlock
import ...Variables: variables, duration
import ...Variables: variables, duration, qval
"""
......@@ -23,25 +23,27 @@ struct FixedGradient <: GradientBlock
Gx :: Vector{Float64}
Gy :: Vector{Float64}
Gz :: Vector{Float64}
function FixedGradient(time::AbstractVector{<:Number}, Gx::AbstractVector{<:Number}, Gy::AbstractVector{<:Number}, Gz::AbstractVector{<:Number})
rotate :: Bool
function FixedGradient(time::AbstractVector{<:Number}, Gx::AbstractVector{<:Number}, Gy::AbstractVector{<:Number}, Gz::AbstractVector{<:Number}; rotate=false)
@assert length(time) == length(Gx)
@assert length(time) == length(Gy)
@assert length(time) == length(Gz)
new(Float64.(time), Float64.(Gx), Float64.(Gy), Float64.(Gz))
new(Float64.(time), Float64.(Gx), Float64.(Gy), Float64.(Gz), rotate)
end
end
function FixedGradient(time::AbstractVector{<:Number}, arr::AbstractVector{<:AbstractVector{<:Number}})
function FixedGradient(time::AbstractVector{<:Number}, arr::AbstractVector{<:AbstractVector{<:Number}}; kwargs...)
@assert all(length.(arr) .== 3)
FixedGradient(
time,
[a[1] for a in arr],
[a[2] for a in arr],
[a[3] for a in arr],
[a[3] for a in arr];
kwargs...
)
end
FixedGradient(time::AbstractVector{<:Number}, Gx::AbstractVector{<:Number}) = FixedGradient(time, Gx, zeros(length(time)), zeros(length(time)))
FixedGradient(time::AbstractVector{<:Number}, Gx::AbstractVector{<:Number}; kwargs...) = FixedGradient(time, Gx, zeros(length(time)), zeros(length(time)); kwargs...)
variables(::Type{<:FixedGradient}) = []
......@@ -49,4 +51,20 @@ duration(fg::FixedGradient) = maximum(fg.time)
Base.show(io::IO, fb::FixedGradient) = print(io, "FixedGradient for $(duration(fb)) ms")
"""
FixedInstantGradient(orientation, qval)
Instantaneous MR gradient with no free variables.
"""
struct FixedInstantGradient <: GradientBlock
orientation :: Any
qval :: Number
end
duraction(instant::FixedInstantGradient) = 0.
qval(instant::FixedInstantGradient) = instant.qval
fixed(f::Union{FixedGradient, FixedInstantGradient}) = f
end
\ No newline at end of file
......@@ -9,9 +9,9 @@ Arbitrary gradient waveforms can be store din a [`ConcreteBlock`](@ref)
"""
module Gradients
include("integrate_gradients.jl")
include("fixed_gradients.jl")
include("pulsed_gradients.jl")
include("instant_gradients.jl")
include("fixed_gradients.jl")
import ..BuildingBlock: GradientBlock
......
module InstantGradients
import JuMP: @constraint, @variable, Model, owner_model
import ...Variables: qval, bval, start_time, duration, variables, get_free_variable, VariableType
import ...BuildingBlocks: GradientBlock
import ...BuildingBlocks: GradientBlock, fixed
import ...ConcreteBlocks: to_concrete_block, AbstractConcreteBlock
import ...BuildSequences: @global_model_constructor
import ..FixedGradients: FixedInstantGradient
"""
InstantGradientBlock(; orientation=:bvec, qval=nothing)
......@@ -40,20 +41,8 @@ bval(instant::InstantGradientBlock) = 0.
duration(instant::InstantGradientBlock) = 0.
variables(::Type{<:InstantGradientBlock}) = [qval]
"""
ConcreteInstantGradient(orientation, qval)
Instantaneous MR gradient with no free variables.
See [`InstantGradientBlock`](@ref) for a version where [`qval`](@ref) is variable.
"""
struct ConcreteInstantGradient <: AbstractConcreteBlock
orientation :: Any
qval :: Number
end
function to_concrete_block(block::InstantGradientBlock)
return ConcreteInstantGradient(block.orientation, value(qval(block)))
function fixed(block::InstantGradientBlock)
return FixedInstantGradient(block.orientation, value(qval(block)))
end
......
......@@ -6,9 +6,10 @@ module PulsedGradients
import JuMP: @constraint, @variable, Model, VariableRef, owner_model, value
import StaticArrays: SVector
import ...Variables: qval, bval, rise_time, flat_time, slew_rate, gradient_strength, variables, duration, δ, get_free_variable, VariableType
import ...BuildingBlocks: GradientBlock, duration, set_simple_constraints!
import ...BuildingBlocks: GradientBlock, duration, set_simple_constraints!, fixed
import ...ConcreteBlocks: ConcreteBlock, to_concrete_block
import ...BuildSequences: @global_model_constructor
import ..FixedGradients: FixedGradient
"""
......@@ -83,7 +84,7 @@ end
variables(::Type{<:PulsedGradient}) = [qval, δ, gradient_strength, duration, rise_time, flat_time, slew_rate]
function to_concrete_block(block::PulsedGradient)
function fixed(block::PulsedGradient)
if block.orientation == :bvec
rotate = true
qvec = [value(qval(block)), 0., 0.]
......@@ -98,12 +99,11 @@ function to_concrete_block(block::PulsedGradient)
end
t_rise = value(rise_time(block))
t_d = value(δ(block))
return ConcreteBlock(t_d + t_rise, gradient=[
(0., zeros(3)),
(t_rise, qvec),
(t_d, qvec),
(t_d + t_rise, zeros(3)),
], rotate_gradient=rotate)
return FixedBlock(
[0., t_rise, t_d, td + t_rise],
[zeros(3), qvec, qvec, zeros(3)];
rotate=rotate
)
end
......
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