From a4b41bdbcbbc1cd85c2137d81dd24477caca88d1 Mon Sep 17 00:00:00 2001 From: Jan Warnking Date: Fri, 20 Sep 2024 06:48:45 +0200 Subject: [PATCH 1/9] Add B1 field to Phantom struct to allow for spatial variations in B1. --- KomaMRIBase/src/datatypes/Phantom.jl | 4 +++- KomaMRIBase/test/runtests.jl | 14 +++++++++----- src/ui/ExportMATFunctions.jl | 4 ++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/KomaMRIBase/src/datatypes/Phantom.jl b/KomaMRIBase/src/datatypes/Phantom.jl index e43297589..403d0f6ef 100644 --- a/KomaMRIBase/src/datatypes/Phantom.jl +++ b/KomaMRIBase/src/datatypes/Phantom.jl @@ -6,7 +6,7 @@ include("phantom/motion/ArbitraryMotion.jl") include("phantom/motion/NoMotion.jl") """ - obj = Phantom(name, x, y, z, ρ, T1, T2, T2s, Δw, Dλ1, Dλ2, Dθ, motion) + obj = Phantom(name, x, y, z, ρ, T1, T2, T2s, Δw, Dλ1, Dλ2, Dθ, motion, B1) The Phantom struct. Most of its field names are vectors, with each element associated with a property value representing a spin. This struct serves as an input for the simulation. @@ -25,6 +25,7 @@ a property value representing a spin. This struct serves as an input for the sim - `Dλ2`: (`::AbstractVector{T<:Real}`) spin Dλ2 (diffusion) parameter vector - `Dθ`: (`::AbstractVector{T<:Real}`) spin Dθ (diffusion) parameter vector - `motion`: (`::MotionModel{T<:Real}`) motion model +- `B1`: (`::AbstractVector{Complex{T<:Real}}`) spin transmit B1 parameter vector # Returns - `obj`: (`::Phantom`) Phantom struct @@ -55,6 +56,7 @@ julia> obj.ρ #Diff::Vector{DiffusionModel} #Diffusion map #Motion motion::MotionModel{T} = NoMotion{eltype(x)}() + B1::AbstractVector{Complex{T}} = Complex.(ones(eltype(x), size(x))) end """Size and length of a phantom""" diff --git a/KomaMRIBase/test/runtests.jl b/KomaMRIBase/test/runtests.jl index d57a4e14f..e42ed5983 100644 --- a/KomaMRIBase/test/runtests.jl +++ b/KomaMRIBase/test/runtests.jl @@ -374,8 +374,9 @@ end Dλ1 = [-4e-6; -2e-6; 0.0; 2e-6; 4e-6] Dλ2 = [-6e-6; -3e-6; 0.0; 3e-6; 6e-6] Dθ = [-8e-6; -4e-6; 0.0; 4e-6; 8e-6] - obj1 = Phantom(name=name, x=x, y=y, z=z, ρ=ρ, T1=T1, T2=T2, T2s=T2s, Δw=Δw, Dλ1=Dλ1, Dλ2=Dλ2, Dθ=Dθ) - obj2 = Phantom(name=name, x=x, y=y, z=z, ρ=ρ, T1=T1, T2=T2, T2s=T2s, Δw=Δw, Dλ1=Dλ1, Dλ2=Dλ2, Dθ=Dθ) + B1 = Complex.([0.8; 0.9; 1.0; 1.1; 1.2]) + obj1 = Phantom(name=name, x=x, y=y, z=z, ρ=ρ, T1=T1, T2=T2, T2s=T2s, Δw=Δw, Dλ1=Dλ1, Dλ2=Dλ2, Dθ=Dθ, B1=B1) + obj2 = Phantom(name=name, x=x, y=y, z=z, ρ=ρ, T1=T1, T2=T2, T2s=T2s, Δw=Δw, Dλ1=Dλ1, Dλ2=Dλ2, Dθ=Dθ, B1=B1) @test obj1 == obj2 # Test size and length definitions of a phantom @@ -571,7 +572,8 @@ end Dλ1, Dλ2, Dθ, - simplemotion + simplemotion, + B1 ) rng = 1:2:5 obs2 = Phantom( @@ -588,6 +590,7 @@ end Dλ2[rng], Dθ[rng], simplemotion[rng], + B1[rng] ) @test obs1[rng] == obs2 @test @view(obs1[rng]) == obs2 @@ -611,13 +614,14 @@ end [Dλ1; Dλ1[rng]], [Dλ2; Dλ2[rng]], [Dθ; Dθ[rng]], - [obs1.motion; obs2.motion] + [obs1.motion; obs2.motion], + [B1; B1[rng]] ) @test obs1 + obs2 == oba # Test scalar multiplication of a phantom c = 7 - obc = Phantom(name=name, x=x, y=y, z=z, ρ=c*ρ, T1=T1, T2=T2, T2s=T2s, Δw=Δw, Dλ1=Dλ1, Dλ2=Dλ2, Dθ=Dθ) + obc = Phantom(name=name, x=x, y=y, z=z, ρ=c*ρ, T1=T1, T2=T2, T2s=T2s, Δw=Δw, Dλ1=Dλ1, Dλ2=Dλ2, Dθ=Dθ, B1=B1) @test c * obj1 == obc #Test brain phantom 2D diff --git a/src/ui/ExportMATFunctions.jl b/src/ui/ExportMATFunctions.jl index e9d6b4d23..adf74c4d0 100644 --- a/src/ui/ExportMATFunctions.jl +++ b/src/ui/ExportMATFunctions.jl @@ -40,8 +40,8 @@ end function export_2_mat_phantom(phantom, matfolder; matfilename="phantom.mat") phantom_dict = Dict("name" => phantom.name, - "columns" => ["x", "y", "z", "rho", "T1", "T2", "T2s", "delta_omega"], - "data" => hcat(phantom.x, phantom.y, phantom.z, phantom.ρ, phantom.T1, phantom.T2, phantom.T2s, phantom.Δw)) + "columns" => ["x", "y", "z", "rho", "T1", "T2", "T2s", "delta_omega", "B1"], + "data" => hcat(phantom.x, phantom.y, phantom.z, phantom.ρ, phantom.T1, phantom.T2, phantom.T2s, phantom.Δw, phantom.B1)) matwrite(joinpath(matfolder, matfilename), Dict("phantom" => phantom_dict)) end From 1790fc8ac5ed6961292e05fd97765446b6f0674e Mon Sep 17 00:00:00 2001 From: Jan Warnking Date: Fri, 20 Sep 2024 07:06:04 +0200 Subject: [PATCH 2/9] Enable plotting of (the real part of) B1 in the UI --- KomaMRIPlots/src/ui/DisplayFunctions.jl | 22 ++++++++++++++-------- src/KomaUI.jl | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/KomaMRIPlots/src/ui/DisplayFunctions.jl b/KomaMRIPlots/src/ui/DisplayFunctions.jl index 4d181c409..b59fb5ca4 100644 --- a/KomaMRIPlots/src/ui/DisplayFunctions.jl +++ b/KomaMRIPlots/src/ui/DisplayFunctions.jl @@ -992,8 +992,8 @@ Plots a phantom map for a specific spin parameter given by `key`. # Arguments - `obj`: (`::Phantom`) Phantom struct -- `key`: (`::Symbol`, opts: [`:ρ`, `:T1`, `:T2`, `:T2s`, `:x`, `:y`, `:z`]) symbol for - displaying different parameters of the phantom spins +- `key`: (`::Symbol`, opts: [`:ρ`, `:T1`, `:T2`, `:T2s`, `:x`, `:y`, `:z`, `:Δw`, `:B1`]) + symbol for displaying different parameters of the phantom spins # Keywords - `height`: (`::Integer`, `=600`) plot height @@ -1074,8 +1074,9 @@ function plot_phantom_map( end path = @__DIR__ - cmin_key = minimum(getproperty(ph, key)) - cmax_key = maximum(getproperty(ph, key)) + this_map = getproperty(ph, key) + cmin_key = minimum(real.(this_map)) # allow for complex maps + cmax_key = maximum(real.(this_map)) if key == :T1 || key == :T2 || key == :T2s cmin_key = 0 factor = 1e3 @@ -1113,6 +1114,11 @@ function plot_phantom_map( factor = 1 / (2π) unit = " Hz" colormap = "Greys" + elseif key == :B1 + factor = 1 + this_map = real.(this_map) + unit = "" + colormap = "Greys" else factor = 1 cmin_key = 0 @@ -1135,7 +1141,7 @@ function plot_phantom_map( y=(y[:, 1]) * 1e2, mode="markers", marker=attr(; - color=getproperty(ph, key) * factor, + color=this_map * factor, showscale=colorbar, colorscale=colormap, colorbar=attr(; ticksuffix=unit, title=string(key)), @@ -1144,7 +1150,7 @@ function plot_phantom_map( size=4, ), showlegend=false, - text=round.(getproperty(ph, key) * factor, digits=4), + text=round.(this_map * factor, digits=4), hovertemplate="x: %{x:.1f} cm
y: %{y:.1f} cm
$(string(key)): %{text}$unit", ), ] @@ -1171,7 +1177,7 @@ function plot_phantom_map( z=(z[:, 1]) * 1e2, mode="markers", marker=attr(; - color=getproperty(ph, key) * factor, + color=this_map * factor, showscale=colorbar, colorscale=colormap, colorbar=attr(; ticksuffix=unit, title=string(key)), @@ -1180,7 +1186,7 @@ function plot_phantom_map( size=2, ), showlegend=false, - text=round.(getproperty(ph, key) * factor, digits=4), + text=round.(this_map * factor, digits=4), hovertemplate="x: %{x:.1f} cm
y: %{y:.1f} cm
z: %{z:.1f} cm
$(string(key)): %{text}$unit", ), ] diff --git a/src/KomaUI.jl b/src/KomaUI.jl index 9c31463b1..4b43e526a 100644 --- a/src/KomaUI.jl +++ b/src/KomaUI.jl @@ -40,7 +40,7 @@ function KomaUI(; darkmode=true, frame=true, phantom_mode="2D", sim=Dict{String, Observables.clear(img_ui) # For phantom sub-buttons - fieldnames_obj = [fieldnames(Phantom)[5:end-3]...] + fieldnames_obj = [fieldnames(Phantom)[[5:end-5;end]]...] # TODO should this be more explicit on which symbols to include rather than using indices? widgets_button_obj = button.(string.(fieldnames_obj)) # Setup the Blink window From 2a1743d982ad38c99e606d8237722c4e9ab59583 Mon Sep 17 00:00:00 2001 From: Jan Warnking Date: Fri, 20 Sep 2024 07:08:11 +0200 Subject: [PATCH 3/9] Preparation for B1 simulation: Allow nxy in Spinor(...) to be complex --- KomaMRICore/src/datatypes/Spinor.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KomaMRICore/src/datatypes/Spinor.jl b/KomaMRICore/src/datatypes/Spinor.jl index 8d8977bbb..a7aae0ad5 100644 --- a/KomaMRICore/src/datatypes/Spinor.jl +++ b/KomaMRICore/src/datatypes/Spinor.jl @@ -149,7 +149,7 @@ IEEE Transactions on Medical Imaging, 10(1), 53-65. doi:10.1109/42.75611 # Arguments - `φ`: (`::Real`, `[rad]`) φ angle -- `nxy`: (`::Real`) nxy factor +- `nxy`: (`::Complex`) nxy factor - `nz`: (`::Real`) nz factor # Returns From bdc969d98dec2184abed6537a7cd54bbf589873e Mon Sep 17 00:00:00 2001 From: Jan Warnking Date: Fri, 20 Sep 2024 07:08:45 +0200 Subject: [PATCH 4/9] Add simulation of B1 map to BlochSimple --- .../src/simulation/SimMethods/BlochSimple/BlochSimple.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/KomaMRICore/src/simulation/SimMethods/BlochSimple/BlochSimple.jl b/KomaMRICore/src/simulation/SimMethods/BlochSimple/BlochSimple.jl index ad0a910c9..53ecaa9af 100644 --- a/KomaMRICore/src/simulation/SimMethods/BlochSimple/BlochSimple.jl +++ b/KomaMRICore/src/simulation/SimMethods/BlochSimple/BlochSimple.jl @@ -84,11 +84,12 @@ function run_spin_excitation!( #Effective field ΔBz = p.Δw ./ T(2π .* γ) .- s.Δf ./ T(γ) # ΔB_0 = (B_0 - ω_rf/γ), Need to add a component here to model scanner's dB0(x,y,z) Bz = (s.Gx .* x .+ s.Gy .* y .+ s.Gz .* z) .+ ΔBz - B = sqrt.(abs.(s.B1) .^ 2 .+ abs.(Bz) .^ 2) + B1 = s.B1 .* p.B1 # Take B1+ transmit RF field map into account. This is complex + B = sqrt.(abs.(B1) .^ 2 .+ abs.(Bz) .^ 2) B[B .== 0] .= eps(T) #Spinor Rotation φ = T(-2π .* γ) .* (B .* s.Δt) # TODO: Use trapezoidal integration here (?), this is just Forward Euler - mul!(Q(φ, s.B1 ./ B, Bz ./ B), M) + mul!(Q(φ, B1 ./ B, Bz ./ B), M) #Relaxation M.xy .= M.xy .* exp.(-s.Δt ./ p.T2) M.z .= M.z .* exp.(-s.Δt ./ p.T1) .+ p.ρ .* (1 .- exp.(-s.Δt ./ p.T1)) From e51fc3e9b4a6d47d6e1b5c41e1ea314235d7fe25 Mon Sep 17 00:00:00 2001 From: Jan Warnking Date: Fri, 20 Sep 2024 07:10:56 +0200 Subject: [PATCH 5/9] Add simulation of B1 map to BlochCPU --- .../src/simulation/SimMethods/Bloch/BlochCPU.jl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/KomaMRICore/src/simulation/SimMethods/Bloch/BlochCPU.jl b/KomaMRICore/src/simulation/SimMethods/Bloch/BlochCPU.jl index 49a6312a8..3c14a16c1 100644 --- a/KomaMRICore/src/simulation/SimMethods/Bloch/BlochCPU.jl +++ b/KomaMRICore/src/simulation/SimMethods/Bloch/BlochCPU.jl @@ -3,6 +3,7 @@ struct BlochCPUPrealloc{T} <: PreallocResult{T} M::Mag{T} # Mag{T} Bz_old::AbstractVector{T} # Vector{T}(Nspins x 1) Bz_new::AbstractVector{T} # Vector{T}(Nspins x 1) + B1::AbstractVector{Complex{T}} # Vector{Complex{T}}(Nspins x 1) ϕ::AbstractVector{T} # Vector{T}(Nspins x 1) Rot::Spinor{T} # Spinor{T} ΔBz::AbstractVector{T} # Vector{T}(Nspins x 1) @@ -13,6 +14,7 @@ Base.view(p::BlochCPUPrealloc, i::UnitRange) = begin p.M[i], p.Bz_old[i], p.Bz_new[i], + p.B1[i], p.ϕ[i], p.Rot[i], p.ΔBz[i] @@ -28,12 +30,13 @@ function prealloc(sim_method::Bloch, backend::KA.CPU, obj::Phantom{T}, M::Mag{T} ), similar(obj.x), similar(obj.x), + similar(obj.x, Complex{eltype(obj.x)}), similar(obj.x), Spinor( similar(M.xy), similar(M.xy) ), - obj.Δw ./ T(2π .* γ) + obj.Δw ./ T(2π .* γ) # Why is this not similar(obj.x)? ) end @@ -119,6 +122,7 @@ function run_spin_excitation!( ) where {T<:Real} Bz = prealloc.Bz_old B = prealloc.Bz_new + B1 = prealloc.B1 φ = prealloc.ϕ α = prealloc.Rot.α β = prealloc.Rot.β @@ -132,12 +136,13 @@ function run_spin_excitation!( x, y, z = get_spin_coords(p.motion, p.x, p.y, p.z, s.t) #Effective field @. Bz = (s.Gx * x + s.Gy * y + s.Gz * z) + ΔBz - s.Δf / T(γ) # ΔB_0 = (B_0 - ω_rf/γ), Need to add a component here to model scanner's dB0(x,y,z) - @. B = sqrt(abs(s.B1)^2 + abs(Bz)^2) + @. B1 = s.B1 * p.B1 # Take B1+ transmit RF field map into account. This is a vector of complex numbers + @. B = sqrt(abs(B1)^2 + abs(Bz)^2) @. B[B == 0] = eps(T) #Spinor Rotation @. φ = T(-π * γ) * (B * s.Δt) # TODO: Use trapezoidal integration here (?), this is just Forward Euler @. α = cos(φ) - Complex{T}(im) * (Bz / B) * sin(φ) - @. β = -Complex{T}(im) * (s.B1 / B) * sin(φ) + @. β = -Complex{T}(im) * (B1 / B) * sin(φ) mul!(Spinor(α, β), M, Maux_xy, Maux_z) #Relaxation @. M.xy = M.xy * exp(-s.Δt / p.T2) From d4f0fc566e2d80d5824bc092fd16f2e17a46bf5e Mon Sep 17 00:00:00 2001 From: Jan Warnking Date: Fri, 20 Sep 2024 07:12:38 +0200 Subject: [PATCH 6/9] Add simulation of B1 map to BlochGPU --- .../src/simulation/SimMethods/Bloch/BlochGPU.jl | 8 ++++++-- .../src/simulation/SimMethods/Bloch/KernelFunctions.jl | 10 +++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/KomaMRICore/src/simulation/SimMethods/Bloch/BlochGPU.jl b/KomaMRICore/src/simulation/SimMethods/Bloch/BlochGPU.jl index 14f78765e..0ec82428a 100644 --- a/KomaMRICore/src/simulation/SimMethods/Bloch/BlochGPU.jl +++ b/KomaMRICore/src/simulation/SimMethods/Bloch/BlochGPU.jl @@ -67,6 +67,7 @@ struct BlochGPUPrealloc{T} <: PreallocResult{T} seq_properties::AbstractVector{SeqBlockProperties{T}} Bz::AbstractMatrix{T} B::AbstractMatrix{T} + B1::AbstractMatrix{Complex{T}} φ::AbstractMatrix{T} ΔT1::AbstractMatrix{T} ΔT2::AbstractMatrix{T} @@ -84,6 +85,7 @@ function prealloc_block(p::BlochGPUPrealloc{T}, i::Integer) where {T<:Real} [seq_block], view(p.Bz,:,1:seq_block.length), view(p.B,:,1:seq_block.length), + view(p.B1,:,1:seq_block.length), view(p.φ,:,1:seq_block.length-1), view(p.ΔT1,:,1:seq_block.length-1), view(p.ΔT2,:,1:seq_block.length-1), @@ -102,6 +104,7 @@ function prealloc(sim_method::Bloch, backend::KA.GPU, obj::Phantom{T}, M::Mag{T} precalc.seq_properties, KA.zeros(backend, T, (size(obj.x, 1), max_block_length)), KA.zeros(backend, T, (size(obj.x, 1), max_block_length)), + KA.zeros(backend, Complex{T}, (size(obj.x, 1), max_block_length)), KA.zeros(backend, T, (size(obj.x, 1), max_block_length-1)), KA.zeros(backend, T, (size(obj.x, 1), max_block_length-1)), KA.zeros(backend, T, (size(obj.x, 1), max_block_length-1)), @@ -180,7 +183,8 @@ function run_spin_excitation!( #Effective Field pre.Bz .= (x .* seq.Gx' .+ y .* seq.Gy' .+ z .* seq.Gz') .+ pre.ΔBz .- seq.Δf' ./ T(γ) # ΔB_0 = (B_0 - ω_rf/γ), Need to add a component here to model scanner's dB0(x,y,z) - pre.B .= sqrt.(abs.(seq.B1') .^ 2 .+ abs.(pre.Bz) .^ 2) + pre.B1 .= seq.B1' .* p.B1 + pre.B .= sqrt.(abs.(pre.B1) .^ 2 .+ abs.(pre.Bz) .^ 2) #Spinor Rotation pre.φ .= T(-π .* γ) .* (pre.B[:,1:end-1] .* seq.Δt') # TODO: Use trapezoidal integration here (?), this is just Forward Euler @@ -190,7 +194,7 @@ function run_spin_excitation!( pre.ΔT2 .= exp.(-seq.Δt' ./ p.T2) #Excitation - apply_excitation!(backend, 512)(M.xy, M.z, pre.φ, seq.B1, pre.Bz, pre.B, pre.ΔT1, pre.ΔT2, p.ρ, ndrange=size(M.xy,1)) + apply_excitation!(backend, 512)(M.xy, M.z, pre.φ, pre.B1, pre.Bz, pre.B, pre.ΔT1, pre.ΔT2, p.ρ, ndrange=size(M.xy,1)) KA.synchronize(backend) return nothing diff --git a/KomaMRICore/src/simulation/SimMethods/Bloch/KernelFunctions.jl b/KomaMRICore/src/simulation/SimMethods/Bloch/KernelFunctions.jl index 1f425ba38..b51fe3583 100644 --- a/KomaMRICore/src/simulation/SimMethods/Bloch/KernelFunctions.jl +++ b/KomaMRICore/src/simulation/SimMethods/Bloch/KernelFunctions.jl @@ -32,13 +32,13 @@ using KernelAbstractions: @kernel, @Const, @index, @uniform, @groupsize, @localm cos_φ = cos(φ[i_g, t]) s_α_r[i_l] = cos_φ if (iszero(B[i_g, t])) - s_α_i[i_l] = -(Bz[i_g, t] / (B[i_g, t] + eps(T))) * sin_φ - s_β_r[i_l] = (imag(B1[t]) / (B[i_g, t] + eps(T))) * sin_φ - s_β_i[i_l] = -(real(B1[t]) / (B[i_g, t] + eps(T))) * sin_φ + s_α_i[i_l] = -(Bz[i_g, t] / (B[i_g, t] + eps(T))) * sin_φ # why not zero? + s_β_r[i_l] = (imag(B1[i_g, t]) / (B[i_g, t] + eps(T))) * sin_φ + s_β_i[i_l] = -(real(B1[i_g, t]) / (B[i_g, t] + eps(T))) * sin_φ else s_α_i[i_l] = -(Bz[i_g, t] / B[i_g, t]) * sin_φ - s_β_r[i_l] = (imag(B1[t]) / B[i_g, t]) * sin_φ - s_β_i[i_l] = -(real(B1[t]) / B[i_g, t]) * sin_φ + s_β_r[i_l] = (imag(B1[i_g, t]) / B[i_g, t]) * sin_φ + s_β_i[i_l] = -(real(B1[i_g, t]) / B[i_g, t]) * sin_φ end s_Mxy_new_r[i_l] = 2 * (s_Mxy_i[i_l] * (s_α_r[i_l] * s_α_i[i_l] - s_β_r[i_l] * s_β_i[i_l]) + s_Mz[i_l] * (s_α_i[i_l] * s_β_i[i_l] + s_α_r[i_l] * s_β_r[i_l])) + From 4ea5c519b4ca29fd9b606c4b76694222a534b8a5 Mon Sep 17 00:00:00 2001 From: Jan Warnking Date: Fri, 20 Sep 2024 12:20:00 +0200 Subject: [PATCH 7/9] Correct conjugate transpose to transpose of B1 in BlochGPU --- KomaMRICore/src/simulation/SimMethods/Bloch/BlochGPU.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KomaMRICore/src/simulation/SimMethods/Bloch/BlochGPU.jl b/KomaMRICore/src/simulation/SimMethods/Bloch/BlochGPU.jl index 0f972b7a3..86a2437f1 100644 --- a/KomaMRICore/src/simulation/SimMethods/Bloch/BlochGPU.jl +++ b/KomaMRICore/src/simulation/SimMethods/Bloch/BlochGPU.jl @@ -189,7 +189,7 @@ function run_spin_excitation!( #Effective Field pre.Bz .= (x .* seq.Gx' .+ y .* seq.Gy' .+ z .* seq.Gz') .+ pre.ΔBz .- seq.Δf' ./ T(γ) # ΔB_0 = (B_0 - ω_rf/γ), Need to add a component here to model scanner's dB0(x,y,z) - pre.B1 .= seq.B1' .* p.B1 + pre.B1 .= transpose(seq.B1) .* p.B1 pre.B .= sqrt.(abs.(pre.B1) .^ 2 .+ abs.(pre.Bz) .^ 2) #Spinor Rotation From f1268068f8a27a2ed3d9a350c3dc795660e3e645 Mon Sep 17 00:00:00 2001 From: Jan Warnking Date: Mon, 23 Sep 2024 18:06:04 +0200 Subject: [PATCH 8/9] Fix B1 plotting in plot_phantom_map method for static phantom --- KomaMRIPlots/src/ui/DisplayFunctions.jl | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/KomaMRIPlots/src/ui/DisplayFunctions.jl b/KomaMRIPlots/src/ui/DisplayFunctions.jl index ee2a1369e..659d52b7c 100644 --- a/KomaMRIPlots/src/ui/DisplayFunctions.jl +++ b/KomaMRIPlots/src/ui/DisplayFunctions.jl @@ -1380,8 +1380,9 @@ function plot_phantom_map( end path = @__DIR__ - cmin_key = minimum(getproperty(obj,key)) - cmax_key = maximum(getproperty(obj,key)) + this_map = getproperty(obj, key) + cmin_key = minimum(real.(this_map)) # allow for complex maps + cmax_key = maximum(real.(this_map)) if key == :T1 || key == :T2 || key == :T2s cmin_key = 0 factor = 1e3 @@ -1409,6 +1410,11 @@ function plot_phantom_map( factor = 1/(2π) unit = " Hz" colormap="Greys" + elseif key == :B1 + factor = 1 + this_map = real.(this_map) + unit = "" + colormap = "Greys" else factor = 1 cmin_key = 0 @@ -1455,7 +1461,7 @@ function plot_phantom_map( y=obj.y*1e2, mode="markers", marker=attr( - color=getproperty(obj,key)*factor, + color=this_map*factor, showscale=colorbar, colorscale=colormap, colorbar=attr(ticksuffix=unit, title=string(key)), @@ -1463,7 +1469,7 @@ function plot_phantom_map( cmax=cmax_key, size=4 ), - text=round.(getproperty(obj,key)*factor,digits=4), + text=round.(this_map*factor,digits=4), hovertemplate="x: %{x:.1f} cm
y: %{y:.1f} cm
$(string(key)): %{text}$unit" ) else @@ -1473,7 +1479,7 @@ function plot_phantom_map( z=obj.z*1e2, mode="markers", marker=attr( - color=getproperty(obj,key)*factor, + color=this_map*factor, showscale=colorbar, colorscale=colormap, colorbar=attr(ticksuffix=unit, title=string(key)), @@ -1481,7 +1487,7 @@ function plot_phantom_map( cmax=cmax_key, size=2 ), - text=round.(getproperty(obj,key)*factor,digits=4), + text=round.(this_map*factor,digits=4), hovertemplate="x: %{x:.1f} cm
y: %{y:.1f} cm
z: %{z:.1f} cm
$(string(key)): %{text}$unit" ) end From dc544332f0ca6328331d577ac5d91f1680416c42 Mon Sep 17 00:00:00 2001 From: Jan Warnking Date: Mon, 23 Sep 2024 18:21:02 +0200 Subject: [PATCH 9/9] Include plotting the B1 map into tests --- KomaMRIPlots/test/GUI_PlotlyJS_backend_test.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/KomaMRIPlots/test/GUI_PlotlyJS_backend_test.jl b/KomaMRIPlots/test/GUI_PlotlyJS_backend_test.jl index f0e2f2aad..90091cf83 100644 --- a/KomaMRIPlots/test/GUI_PlotlyJS_backend_test.jl +++ b/KomaMRIPlots/test/GUI_PlotlyJS_backend_test.jl @@ -30,6 +30,11 @@ @test true #If the previous line fails the test will fail end + @testset "plot_phantom_map_B1" begin + plot_phantom_map(ph, :B1) #Plotting the phantom's rho map + @test true #If the previous line fails the test will fail + end + @testset "plot_phantom_map_2dview" begin plot_phantom_map(ph, :ρ, view_2d=true) #Plotting the phantom's rho map @test true #If the previous line fails the test will fail @@ -65,6 +70,11 @@ @test true #If the previous line fails the test will fail end + @testset "plot_motion_phantom_map_B1" begin + plot_phantom_map(ph, :B1) #Plotting the phantom's rho map + @test true #If the previous line fails the test will fail + end + @testset "plot_motion_phantom_map_2dview" begin plot_phantom_map(ph, :ρ, view_2d=true) #Plotting the phantom's rho map @test true #If the previous line fails the test will fail