From 1427f18a14ca7978a0cf8d0d6064a4c54ba928e5 Mon Sep 17 00:00:00 2001 From: Michiel Cottaar <MichielCottaar@protonmail.com> Date: Mon, 20 May 2024 15:53:13 +0100 Subject: [PATCH] Write MRIBuilder.Sequence to pulseq file --- src/sequence_io/pulseq.jl | 88 +++++++++++++++++-- .../pulseq_io/parsers/definitions.jl | 1 + src/sequence_io/pulseq_io/pulseq_io.jl | 1 + src/sequence_io/sequence_io.jl | 12 +-- 4 files changed, 92 insertions(+), 10 deletions(-) diff --git a/src/sequence_io/pulseq.jl b/src/sequence_io/pulseq.jl index 4477b6e..fd25519 100644 --- a/src/sequence_io/pulseq.jl +++ b/src/sequence_io/pulseq.jl @@ -3,14 +3,16 @@ Module converting MRIBuilder sequences to and from sequences recognised by [`Pul """ module Pulseq import Interpolations: linear_interpolation -import ..PulseqIO.Types: PulseqSequence, PulseqBlock, PulseqTrapezoid, PulseqGradient -import ...Containers: Sequence, BuildingBlock -import ...Components: GenericPulse, ADC +import ..PulseqIO.Types: PulseqSequence, PulseqBlock, PulseqTrapezoid, PulseqGradient, PulseqRFPulse, PulseqShape, PulseqADC, PulseqExtension +import ...Containers: Sequence, BuildingBlock, BaseBuildingBlock, events, waveform, iter_blocks +import ...Components: GenericPulse, ADC, RFPulseComponent import ...Scanners: Scanner +import ...Variables: duration, nsamples, dwell_time, make_generic -function Sequence(pulseq::PulseqSequence; scanner=nothing, B0=3) +function Sequence(pulseq::PulseqSequence; scanner=nothing, B0=nothing) if isnothing(scanner) - scanner = Scanner(B0=B0) + use_B0 = isnothing(B0) ? get(pulseq.definitions, :B0, 3.) : B0 + scanner = Scanner(B0=use_B0) end blocks = BuildingBlock.(pulseq.blocks; pulseq.definitions...) return Sequence(blocks; name=Symbol(get(pulseq.definitions, :name, "from_pulseq")), scanner=scanner) @@ -88,4 +90,80 @@ function _get_amplitude(grad::PulseqGradient, time::Number, raster::Number) end +function PulseqSequence(seq::Sequence{S}) where {S} + definitions = ( + name=S, + AdcRasterTime=1e-9, + BlockDurationRaster=1e-9, + RadiofrequencyRasterTime=1e-9, + GradientRasterTime=1e-9, + TotalDuration=duration(seq) * 1e-3, + B0=seq.scanner.B0, + ) + blocks = [PulseqBlock(block; definitions...) for (_, block) in iter_blocks(seq)] + return PulseqSequence( + v"1.4.0", + definitions, + blocks + ) +end + +function PulseqBlock(block::BaseBuildingBlock; BlockDurationRaster, AdcRasterTime, kwargs...) + rf = nothing + adc = nothing + for (delay, event) in events(block) + if event isa RFPulseComponent + if !isnothing(rf) + error("Pulseq does not support a single building block containing multiple RF pulses.") + end + gen = make_generic(event) + rf = PulseqRFPulse( + maximum(gen.amplitude) * 1e3, + PulseqShape(gen.amplitude ./ maximum(gen.amplitude)), + PulseqShape(deg2rad.(gen.phase)), + PulseqShape(gen.time .* 1e-3), + Int(div(delay, 1e-3)), + 0., + 0. + ) + elseif event isa ADC + if !isnothing(rf) + error("Pulseq does not support a single building block containing multiple ADC events.") + end + adc = PulseqADC( + nsamples(event), + div(dwell_time(event), AdcRasterTime), + Int(div(delay, 1e-3)), + 0., 0. + ) + else + error("Cannot write $(typeof(event)) to Pulseq.") + end + end + + grads = [] + times = [t for (t, _) in waveform(block)] + for dim in 1:3 + amplitudes = [g[dim] for (_, g) in waveform(block)] + if iszero(maximum(abs.(amplitudes); init=0.)) + push!(grads, nothing) + else + push!(grads, PulseqGradient( + maximum(amplitudes) * 1e3, + PulseqShape(amplitudes ./ maximum(amplitudes)), + PulseqShape(times .* 1e-3), + 0., + )) + end + end + + return PulseqBlock( + Int(div(1e-3 * duration(block), BlockDurationRaster)), + rf, + grads..., + adc, + Tuple{PulseqExtension, Int}[] + ) +end + end \ No newline at end of file diff --git a/src/sequence_io/pulseq_io/parsers/definitions.jl b/src/sequence_io/pulseq_io/parsers/definitions.jl index 2da9962..5ba67c7 100644 --- a/src/sequence_io/pulseq_io/parsers/definitions.jl +++ b/src/sequence_io/pulseq_io/parsers/definitions.jl @@ -4,6 +4,7 @@ function parse_section(section:: PulseqSection{:definitions}; kwargs...) end _to_string_value(value::AbstractString) = value +_to_string_value(value::Symbol) = string(value) _to_string_value(value::Number) = string(value) _to_string_value(value::Vector) = join(_to_string_value.(value), " ") diff --git a/src/sequence_io/pulseq_io/pulseq_io.jl b/src/sequence_io/pulseq_io/pulseq_io.jl index d60141e..278e207 100644 --- a/src/sequence_io/pulseq_io/pulseq_io.jl +++ b/src/sequence_io/pulseq_io/pulseq_io.jl @@ -12,6 +12,7 @@ include("components.jl") include("parsers/parsers.jl") include("parse_sections.jl") +import .Types: PulseqSequence """ diff --git a/src/sequence_io/sequence_io.jl b/src/sequence_io/sequence_io.jl index 062a156..e0ebd01 100644 --- a/src/sequence_io/sequence_io.jl +++ b/src/sequence_io/sequence_io.jl @@ -4,7 +4,7 @@ include("pulseq_io/pulseq_io.jl") include("pulseq.jl") import Serialization: serialize, deserialize -import .PulseqIO: read_pulseq +import .PulseqIO: read_pulseq, write_pulseq, PulseqSequence import ..Containers: Sequence @@ -14,7 +14,7 @@ function read_sequence(filename::AbstractString, args...; kwargs...) end end -function read_sequence(io::IO; format=nothing, kwargs...) +function read_sequence(io::IO; format=nothing, B0=nothing, scanner=nothing,kwargs...) if isnothing(format) pos = position(io) for format in (:pulseq, :serialize) @@ -27,7 +27,7 @@ function read_sequence(io::IO; format=nothing, kwargs...) error("Could not read the input filename. Tried all formats (:pulseq/:serialize).") end if format == :pulseq - return Sequence(read_pulseq(io, kwargs...)) + return Sequence(read_pulseq(io, kwargs...), B0=B0, scanner=scanner) elseif format == :serialize return deserialize(io, kwargs...) else @@ -42,8 +42,10 @@ function write_sequence(filename::AbstractString, args...; kwargs...) end end -function write_sequence(io::IO, sequence; format::Symbol=:serialize) - if format == :serialize +function write_sequence(io::IO, sequence; format::Symbol=:pulseq) + if format == :pulseq + write_pulseq(io, PulseqSequence(sequence)) + elseif format == :serialize serialize(io, sequence) else error("Unrecognised selected format for output ($format). Only :serialize is supported..") -- GitLab