GithubHelp home page GithubHelp logo

juliahealth / komamri.jl Goto Github PK

View Code? Open in Web Editor NEW
109.0 4.0 18.0 458.6 MB

Koma is a Pulseq-compatible framework to efficiently simulate Magnetic Resonance Imaging (MRI) acquisitions. The main focus of this package is to simulate general scenarios that could arise in pulse sequence development.

Home Page: https://JuliaHealth.github.io/KomaMRI.jl/dev/

License: MIT License

Julia 94.41% CSS 1.74% HTML 3.78% JavaScript 0.06%
mri simulation diffusion diffusion-mri cardiac gpu-acceleration

komamri.jl's People

Contributors

atrotier avatar beorostica avatar cncastillo avatar curtcorum avatar dependabot[bot] avatar dilumaluthge avatar gabuzi avatar gsahonero avatar pabloirarrazaval avatar pvillacorta avatar rkierulf avatar thecedarprince avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

komamri.jl's Issues

[Important] Simulation inconsistency in GUI due to thread racing condition

If you run the simulation multiple times in the GUI there are some inconsistencies in the phase. Plotting the real value of the signal below:

image

This also affects the reconstructed phase:

image

Possible causes:

    1. The raw data is modified each run. Solution: change those lines to use "copies" of the object instead.
    1. The interpolation object is being modified for each thread, causing problems. Solution: remove interpolation.
    1. Synchronization problems when dealing with threads? Solution: Use @sync macro (?)

We must check if this also happens when simulating normally (no UI, case 2 and 3).

fftc not defined

In the version 0.6.4 I commented the reconstruction functions and generated a new bug 😓. We need to test the whole pipeline in the CI.

Signal was being conjugated (affected "return_type" => "raw")

In the BlochSimulationMethod I was doing:

    sig .= sum(Mxy[:, findall(seq.ADC)]; dims=1)'

The ' operator not only transpose but also conjugates, and to compensate, I was applying the rotations without the negative sign (anti-clockwise). While this did not affect the accuracy of the simulations, it was a mistake and I changed it to:

    sig .= transpose(sum(Mxy[:, findall(seq.ADC)]; dims=1))

Now the direction of all the rotations is clockwise like it is supposed to be.

I realized this as the rotation orientation was correct if the output of the simulation was the SpinStateRepresentation like in simulate_slice_profile:

using KomaMRI, PlotlyJS
seq = PulseDesigner.RF_sinc(B1, Trf, sys; G=[0;0;Gz], TBP=8)
α = KomaMRI.get_flip_angles(seq)[1]
α_desired = 30 + 0im
seq = (α_desired / α) * seq #Scaling pusle to have a flip angle of 120
plot_seq(seq; max_rf_samples=Inf)

simParams = Dict{String, Any}("Δt_rf" => Trf / length(seq.RF.A[1]))
M = simulate_slice_profile(seq; z, simParams)

f = γ * Gz * z
p1 = scatter(x=f, y=real.(M.xy), name="Mx")
p2 = scatter(x=f, y=imag.(M.xy), name="My")
p3 = scatter(x=f, y=M.z, name="Mz")
plot([p1,p2,p3])

This replicates the example shown in the "Handbook of MRI Pulse Sequences", Bernstein:

image

SpinLab UI not showing correctly in Windows

In Windows there is a mix of slashes and backslashes, example: "/assetserver/...-assets\Logo.png". Fixed by replacing joinpath function with string concatenation, like image= assets*"/image.png".

RF implementation with GPU acceleration

For now, the file Simulator.jl has a dummy function run_spin_excitation_parallel that has to be developed. It is inserted in the function run_sim2D_times_iter (to be renamed):

@showprogress for p  parts
    if is_RF_on(seq, t[p])
        M0 = run_spin_excitation_parallel(obj, seq, t[p]; M0)
    else
        S[p], M0 = run_spin_precession_parallel(obj, seq, t[p]; M0)
    end
end

Julia 1.7.3 breaks Gradient and RF hcat

When running a function that uses ;; to concatenate, like:

RF_hard(B1, T, sys::Scanner; G=[0,0,0]) = begin
	ζ = sum(G) / sys.Smax
	EX = Sequence([	Grad(G[1],T,ζ);	 #Gx
					Grad(G[2],T,ζ);  #Gy
					Grad(G[3],T,ζ);;], #Gz
					 [RF(B1,T,0,ζ)]	 #RF
					)
	EX
end

Julia 1.7.3 throws an error:

julia> PulseDesigner.RF_hard(1.,1.,Scanner())
ERROR: MethodError: no method matching size(::Grad, ::Int64)
Closest candidates are:
  size(::Union{LinearAlgebra.QR, LinearAlgebra.QRCompactWY, LinearAlgebra.QRPivoted}, ::Integer) at /opt/julia-1.7.3/share/julia/stdlib/v1.7/LinearAlgebra/src/qr.jl:566
  size(::Union{LinearAlgebra.Cholesky, LinearAlgebra.CholeskyPivoted}, ::Integer) at /opt/julia-1.7.3/share/julia/stdlib/v1.7/LinearAlgebra/src/cholesky.jl:495
  size(::Union{LinearOperators.AdjointLinearOperator, LinearOperators.TransposeLinearOperator}, ::Int64) at ~/.julia/packages/LinearOperators/58FwN/src/adjtrans.jl:63
  ...
Stacktrace:
 [1] _typed_hvncat_dims(#unused#::Type{Grad}, dims::Tuple{Int64, Int64}, row_first::Bool, as::Tuple{Grad, Grad, Grad})
   @ Base ./abstractarray.jl:2328
 [2] _typed_hvncat
   @ ./abstractarray.jl:2304 [inlined]
 [3] _hvncat
   @ ./abstractarray.jl:2133 [inlined]
 [4] hvncat
   @ ./abstractarray.jl:2129 [inlined]
 [5] RF_hard(B1::Float64, T::Float64, sys::Scanner; G::Vector{Int64})
   @ Koma.PulseDesigner ~/Documents/Koma.jl/src/sequences/PulseDesigner.jl:11
 [6] RF_hard(B1::Float64, T::Float64, sys::Scanner)
   @ Koma.PulseDesigner ~/Documents/Koma.jl/src/sequences/PulseDesigner.jl:10
 [7] top-level scope
   @ REPL[3]:1

CUDA memory problems?

I see a weird problem with memory, after a simulation it seems that the GPU memory keeps being used even after the finishing:

image

I could just add GC.gc(true) and CUDA.reclaim() at the end of the simulation but I shouldn't need to.

Permission error when trying to write temporary files

For now, to update the paths of the assets I was writing a temporary file with the absolute path. This causes some issues when done in a directory lacking the permissions.

Need to change the way the asset paths are handled.

Separate KomaMRI into simpler subpackages

This needs to be done to remove a lot of the dependencies of the UI like Blink, PlotlyJS, etc.

The idea of doing this is to reduce the number of dependencies to the minimum if someone just wants to simulate data.

To replace MRIReco, the KomaMRIBase package should use instead:
MRIBase
-> RawAcquisitionData
-> AcquisitionData
MRIFiles
-> ISMRMRD

Then, the KomaMRI package can have other dependencies.

Not sure if the plots for our datatypes should be included in KomaMRIBase. Maybe also have KomaMRIPlots?

In summary:
├── KomaMRI.jl (depends on Base and UI)
│ ├── KomaMRIBase.jl (least dependencies)
│ └── KomaMRIPlots.jl (separated to include plots only if needed)

We will need to think about the best way to do this.


Subpackages:

Big simulation bug

While comparing JEMRIS with MRIsim using the exact same phantom and sequence, I found a weird simulation bug:
image
There was a ghost-like artifact appearing, this artifact vanished as I decreased Δt.

Gradient visualization

I need to add the possibility of visualizing the following:

  • Gradient moments (already implemented in the function plot_grads_moments in Display.jl),
  • Proper k-space display,
  • PNS,

Interact file picker error

The file picker on ReconGUI.jl:7 is throwing an error. Commented lines for now.

LoadError: ArgumentError: No file exists at given path: 

Stacktrace:

  [1] checkpath_load(file::String)

    @ FileIO ~/.julia/packages/FileIO/FUXWu/src/loadsave.jl:167

  [2] load(file::FileIO.Formatted, args::String; options::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})

    @ FileIO ~/.julia/packages/FileIO/FUXWu/src/loadsave.jl:117
...

Annoying random update of plots in GUI

Sometimes when you are in a tab and load a phantom object, the phantom plot appears multiple times (it should not appear at all). I think the culprits are the Observables variables, more research is needed to determine the cause.

For now, what I have been doing to avoid this bug, is to separate the plots into multiple tabs/ sub-menus. Are the buttons the problem?

Exporting results to .mat from the GUI

I think it would be handy to be able to easily export .mat files directly from the GUI (for the signal and recon). An additional button can be added to the Plotly.js plots, but to map it to a MAT export GUI is something that needs to be researched further.

  • The signal exporter must include the raw data, the k-space trajectory, and the simulation dict.
  • The recon exporter must include the reconstructed image and the recon dict.

Issue in MRIReco with NFFTTools 0.2.4

After a patch change in the NFFTTools dependency, the MRIreco package is not working properly.

In practice, in the KomaUI() it is not possible to reconstruct the image from the "Raw Data" after clicking the "Recontruct!" button and the process gets stuck indefinitely.

The problem has been documented in the MRIreco pull request MagneticResonanceImaging/MRIReco.jl#95 and it is also solved, however it has not been merged into the master branch yet.

One solution it is just to wait until MRIreco merges its pull request. Another solution would be downgrade the MRIreco version from 0.5.0 to 0.4.2.

Next steps to simulate cardiac MRI

This can be divided into two problems:

Phantom

  • Currently, there is no ECG signal or RR intervals in the Phantom object. Adding a parameter for this in the Phantom object is necessary.
  • We need to creat a cardiac phantom, probably based on the XCAT phantom (talk with Claudia Prieto). The main problem, in this case, is how to obtain the displacement fields $\boldsymbol{u}(\boldsymbol{x},t)$, as XCAT just provides multiple images for every cardiac phase without a way to "follow the spins".
  • A more challenging problem is how to incorporate the RR intervals to accelerate or slow down the movement of the Phantom. My suggestion is that the phantom displacement field is defined for a normal heart rate of 1s, and then for each heartbeat, the displacement field is changed based on the RR [s], or $\boldsymbol{u}\left(\boldsymbol{x},\frac{1}{\mathrm{RR}_i}t\right)$.

Pulseq

  • The Pulseq reader is lacking the implementation of the Extensions "EXT". This is where the cardiac triggering is done.
  • We also need to add in the PulseDesigner module the ability to create triggers like in the MATLAB implementation of Pulseq: trig=mr.makeTrigger('physio1','duration', 2000e-6);.

Simulation

  • The simulator must use the RR intervals (if present) of the Phantom object to time the Sequence. This means the delays of some parts of the Sequence should be replaced based on the RR intervals.

Python API

As suggested in the ISMRM 2022, we should have a Python API to make the generation of data easier for Deep Learning models. I think this should be straightforward but need to look it up.

Widget bugs

Loading a file with the same name does not update the internal global variables. I think this is a problem with the Observable used to load the file, as the variable is not changing the action is not triggered. Is there a solution for this?

EDIT: I just want to be explicit with this:

TagBot trigger issue

This issue is used to trigger TagBot; feel free to unsubscribe.

If you haven't already, you should update your TagBot.yml to include issue comment triggers.
Please see this post on Discourse for instructions and more details.

If you'd like for me to do this for you, comment TagBot fix on this issue.
I'll open a PR within a few hours, please be patient!

Edge cases arise during simple 1-spin simulation

In summary, we should review the following:

Description

While testing a phantom with 1-spin and 90-degree RF pulse, given by the following script:

# Import modules
using KomaMRI

# Define the scanner struct
sys = Scanner()

# Define the 1-spin phantom struct with T1 and T2 parameters
T1, T2 = 1, .1
obj = Phantom(x=[0], T1=[T1], T2=[T2])

# Define the 90-degree hard excitation pulse parameters
ampRF = sys.B1                      # amplitude of the RF pulse
durRF = π / 2 / (2π * γ * ampRF)    # duration of the RF pulse

# Define the 90-degree hard excitation pulse parameters
nADC = 16           # number of acquisition samples
durADC = 5 * T2     # duration of the acquisition 

# Define sequence struct
matGRs  = [Grad(0, durRF)    Grad(0, durADC);
           Grad(0, durRF)    Grad(0, durADC);
           Grad(0, durRF)    Grad(0, durADC)]
matRFs  = [RF(ampRF, durRF)  RF(0, durADC)]
vecADCs = [ADC(0, durRF);    ADC(nADC, durADC)]
seq = Sequence(matGRs, matRFs, vecADCs)

# Plot the sequence
p = plot_seq(seq; slider=true, height=300);
savefig(p, "seq.html");

# Perform the simulation and get the output raw signal
sig = simulate(obj, seq, sys)

, the simulation() function throws an error related with the Interpolation:

julia> sig = simulate(obj, seq, sys)

[ Info: Running simulation... [GPU = true, CPU = 1 thread(s)].
Dividing simulation in Nblocks=4
Starting simulation with Nspins=1 and Nt=515
ERROR: BoundsError: attempt to access 515-element extrapolate(interpolate((::Vector{Float64},), ::Vector{ComplexF64}, Gridded(Linear())), Throw()) with element type ComplexF64 at index [0.500587165012496]
Stacktrace:
  [1] throw_boundserror(A::Interpolations.Extrapolation{ComplexF64, 1, Interpolations.GriddedInterpolation{ComplexF64, 1, ComplexF64, Interpolations.Gridded{Interpolations.Linear{Interpolations.Throw{Interpolations.OnGrid}}}, Tuple{Vector{Float64}}}, Interpolations.Gridded{Interpolations.Linear{Interpolations.Throw{Interpolations.OnGrid}}}, Interpolations.Throw{Nothing}}, I::Tuple{Float64})
    @ Base .\abstractarray.jl:691
  [2] inbounds_index
    @ C:\Users\beorostica\.julia\packages\Interpolations\y4lLj\src\extrapolation\extrapolation.jl:111 [inlined]
  [3] inbounds_position
    @ C:\Users\beorostica\.julia\packages\Interpolations\y4lLj\src\extrapolation\extrapolation.jl:102 [inlined]
  [4] (::Interpolations.Extrapolation{ComplexF64, 1, Interpolations.GriddedInterpolation{ComplexF64, 1, ComplexF64, Interpolations.Gridded{Interpolations.Linear{Interpolations.Throw{Interpolations.OnGrid}}}, Tuple{Vector{Float64}}}, Interpolations.Gridded{Interpolations.Linear{Interpolations.Throw{Interpolations.OnGrid}}}, Interpolations.Throw{Nothing}})(x::Float64)
    @ Interpolations C:\Users\beorostica\.julia\packages\Interpolations\y4lLj\src\extrapolation\extrapolation.jl:48
  [5] Extrapolation
    @ C:\Users\beorostica\.julia\packages\Interpolations\y4lLj\src\extrapolation\extrapolation.jl:61 [inlined]
  [6] run_sim_time_iter(obj::Phantom, seq::Sequence, t::Vector{Float64}, Δt::Vector{Float64}; Nblocks::Int64, Nthreads::Int64, gpu::Function, w::Nothing)
    @ KomaMRI C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\simulation\SimulatorCore.jl:200
  [7] macro expansion
    @ .\timing.jl:220 [inlined]
  [8] simulate(obj::Phantom, seq::Sequence, sys::Scanner; simParams::Dict{String, Any}, w::Nothing)
    @ KomaMRI C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\simulation\SimulatorCore.jl:228
  [9] simulate(obj::Phantom, seq::Sequence, sys::Scanner)
    @ KomaMRI C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\simulation\SimulatorCore.jl:206
 [10] top-level scope
    @ REPL[430]:1
 [11] top-level scope
    @ C:\Users\beorostica\.julia\packages\CUDA\DfvRa\src\initialization.jl:52

This problem can be overcome by adding a gradient with amplitude different from zero at the end of the sequence, like in the following script:

# Import modules
using KomaMRI

# Define the scanner struct
sys = Scanner()

# Define the 1-spin phantom struct with T1 and T2 parameters
T1, T2 = 1, .1
obj = Phantom(x=[0], T1=[T1], T2=[T2])

# Define the 90-degree hard excitation pulse parameters
ampRF = sys.B1                      # amplitude of the RF pulse
durRF = π / 2 / (2π * γ * ampRF)    # duration of the RF pulse

# Define the 90-degree hard excitation pulse parameters
nADC = 16           # number of acquisition samples
durADC = 5 * T2     # duration of the acquisition 

# Define the parameters for the last block of the sequence
ampGR = .1 * ampRF      # amplitude of the gradient in the last block
durGR = .1 * durRF      # duration of the gradient in the last block

# Define sequence struct
matGRs  = [Grad(0, durRF)    Grad(0, durADC)     Grad(ampGR, durGR);
           Grad(0, durRF)    Grad(0, durADC)     Grad(ampGR, durGR);
           Grad(0, durRF)    Grad(0, durADC)     Grad(ampGR, durGR)]
matRFs  = [RF(ampRF, durRF)  RF(0, durADC)       RF(0, durGR)]
vecADCs = [ADC(0, durRF);    ADC(nADC, durADC);  ADC(0, durGR)]
seq = Sequence(matGRs, matRFs, vecADCs)

# Plot the sequence
p = plot_seq(seq; slider=true, height=300);
savefig(p, "seq.html");

# Perform the simulation and get the output raw signal
sig = simulate(obj, seq, sys)

# Transform the output raw signalt to ismrmrd format (::RawAcquisitionData)
seq.DEF["Nx"], seq.DEF["Ny"], seq.DEF["Nz"] = 1, 1, 1
ismrmrd = rawSignalToISMRMRD([sig;;], seq; phantom=obj, sys=sys)

# Plot the output raw signal
p = plot_signal(ismrmrd; height=300);
savefig(p, "ismrmrd.html");

which gives the following exponential decay output raw signal:
1spin-signal-1

This problem could be resolved by adding the extra argument extrapolation_bc=0 to the function LinearInterpolation() which is inside the function run_sim_time_iter(). However, I changed that in a rapid test and the simulation() threw an error.

It is also weird that in this example the first sample of the acquisition doesn't start at 1, this can be patched in the example by adding a delay in the ADC like so:

delayADC = .001     # a small delay
vecADCs = [ADC(0, durRF); ADC(nADC, durADC, delayADC); ADC(0, durGR)]

, which gives the following expected exponential decay output raw signal that start from ~1:
1spin-signal-2

This issue is also related with the linear interpolation performed to get equally spaced samples, however, apparently, this is not necessary anymore.

Additionally, by using one sample for the acquisition in the previous example:

nADC = 1            # number of acquisition samples

, the function simulation() throws the following error:

julia> sig = simulate(obj, seq, sys)
ERROR: ArgumentError: range(0.0, stop=0.05, length=1): endpoints differ
Stacktrace:
  [1] _linspace1(#unused#::Type{Float64}, start::Float64, stop::Float64, len::Int64)
    @ Base .\twiceprecision.jl:726
  [2] range_start_stop_length(start::Float64, stop::Float64, len::Int64)
    @ Base .\twiceprecision.jl:635
  [3] range_start_stop_length
    @ .\range.jl:526 [inlined]
  [4] _range
    @ .\range.jl:128 [inlined]
  [5] #range#59
    @ .\range.jl:111 [inlined]
  [6] get_sample_times(seq::Sequence)
    @ KomaMRI C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\datatypes\sequence\ADC.jl:36
  [7] get_variable_times(seq::Sequence; dt::Float64, dt_rf::Float64)
    @ KomaMRI C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\simulation\TimeStepCalculation.jl:50
  [8] #get_uniform_times#171
    @ C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\simulation\TimeStepCalculation.jl:36 [inlined]
  [9] simulate(obj::Phantom, seq::Sequence, sys::Scanner; simParams::Dict{String, Any}, w::Nothing)
    @ KomaMRI C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\simulation\SimulatorCore.jl:211
 [10] simulate(obj::Phantom, seq::Sequence, sys::Scanner)
    @ KomaMRI C:\Users\beorostica\.julia\packages\KomaMRI\M3OUj\src\simulation\SimulatorCore.jl:206
 [11] top-level scope
    @ REPL[965]:1
 [12] top-level scope
    @ C:\Users\beorostica\.julia\packages\CUDA\DfvRa\src\initialization.jl:52

Bug in the time step calculation

In version 0.3.7 I introduced a bug by calculating incorrectly the size of a seq. I used size(seq,1) (not defined) instead of size(seq)[1].

Documentation minor tasks

We need to complete a few things for the documentation. Suggestions below:

Gradients

  • #270
  • Arbitrary waveform definition with a plot.
  • Concatenation of Grad objects in x, y, and z with a plot.
  • Concatenation of Grad objects in time with a plot.
  • Gradient rotation example with a plot.
  • Advanced example: Moment compensated diffusion.

RF

  • Basic hard-RF usage, parameter definition with a plot.
  • Arbitrary waveform definition with a plot (soft-RF pulses).
  • Concatenation of RF objects in coil dimension with a plot (NOT IMPLEMENTED YET).
  • Concatenation of RF objects in time with a plot.
  • Advanced example: Designing a pulse using the Shinnar Le-Reux transform.

ADC

  • Basic ADC usage, parameter definition with a plot.
  • Mention the phase compensation used in the signal related to the ADC phase (necessary for RF-spoiled sequences).

Sequence

  • Basic Sequence definition GR, RF, ADC. Plot sequence.
  • M0 and k-space display for the sequence.
  • Loading a Sequence using a Pulseq file.
  • Writing a Pulseq sequence (not implemented yet in Koma, but MATLAB's implementation could be shown)
  • Writing a sequence as a JLD2 file.
  • Rotation of the FOV using rotation matrices applied to Sequence.

Phantom

  • Basic Phantom definition with plot.
  • Loading a phantom using a JEMRIS .h5.

Simulation examples

  • FID, showing how to plot the signal (maybe from an ISMRMRD file).
  • Changing the simulation parameters (and explanation of the params).
  • ISMRMRD export.
  • Dictionary generation (like FID but with more RF pulses only for balanced sequences).
  • RF slice excitation (slice_profile function).
  • Examples paper: Chemical shift, Susceptibility, Motion, MRF.

Reconstruction examples

  • Changing the parameters, show TV regularized example (MRIReco.jl example).

GUI

  • How to load sequences, phantoms, and scanners and then simulate.
  • Standalone reconstruction (loading ISMRMRD file to reconstruct).
  • Exporting results as a .mat (NOT IMPLEMENTED YET).
  • Exporting results as a .svg.
  • Changing simulation and reconstruction parameters in the GUI (the same examples as before but with the GUI).
  • Advanced: Add a button to the GUI (or modify something), showcase that is mainly HTML, and show the usage of the @js_ functions to call Julia (the simulator).

Advanced:warning:

  • At least one example showing how to extend the Phantom object and/or the simulator is needed. We could have as an example Monte-Carlo simulations for diffusion.
  • An example of how to run Koma on a server could be useful. Even if it exactly the same as running it normally, mentioning it explicitly is important.

Spoilers generate spurious echoes

Spurious echoes are generated in the same direction as the gradient spoilers. I am not sure if this is expected due to the lack of T2*-decay, or an effect that arises due to the lack of "continuous spins" (like in Extended Phase Graphs), or both.

Interleaved spiral acquisition:
image
Results in k-space:
image
Comparison of the signal with JEMRIS:
image

T2* decay

As it is right now, the simulator does not take into account T2* decay. This intra-voxel perturbation of spins' off-resonance could be simulated easily by generating randomly distributed spins with off-resonance following a Lorentzian distribution (so in the time-domain the decay is exponential). Nevertheless, this is very inefficient computationally and adds unnecessary complexity.

To simulate efficiently, we could track the "width" of the Lorentzian distribution through time and calculate a decay factor E_T2*. This is basically equivalent to changing the spin model to a spin distribution model, where instead of just storing Mxy and Mz we also stored the FWHM of the phase distribution dPhi.

Maybe we can combine that with EPGs to solve #52 as well.

Theory of T2:*
Microstructural off-resonance distributions.pdf

Plot functions throw error in Julia REPL

The following plot functions defined in the DisplayFunctions.jl file:

  • plot_seq()
  • plot_kspace()
  • plot_phantom_map()
  • plot_signal()

, don't open a window to display the plots and throw the same error in the Julia REPL:

julia> plot_seq(seq)
Error showing value of type PlotlyJS.SyncPlot:
ERROR: MethodError: no method matching *(::Nothing, ::Float64)
Closest candidates are:
  *(::Any, ::Any, ::Any, ::Any...) at C:\Users\beorostica\AppData\Local\Programs\Julia-1.7.3\share\julia\base\operators.jl:655
  *(::StridedArray{P}, ::Real) where P<:Dates.Period at C:\Users\beorostica\AppData\Local\Programs\Julia-1.7.3\share\julia\stdlib\v1.7\Dates\src\deprecated.jl:44
  *(::Union{SparseArrays.SparseVector{Tv, Ti}, SubArray{Tv, 1, <:SparseArrays.AbstractSparseVector{Tv, Ti}, Tuple{Base.Slice{Base.OneTo{Int64}}}, false}, SubArray{Tv, 1, <:SparseArrays.AbstractSparseMatrixCSC{Tv, Ti}, Tuple{Base.Slice{Base.OneTo{Int64}}, Int64}, false}} where {Tv, Ti}, ::Number) at C:\Users\beorostica\AppData\Local\Programs\Julia-1.7.3\share\julia\stdlib\v1.7\SparseArrays\src\sparsevector.jl:1474
  ...
Stacktrace:
  [1] display_blink(p::PlotlyJS.SyncPlot)
    @ PlotlyJS C:\Users\beorostica\.julia\packages\PlotlyJS\4jzLr\src\display.jl:165
  [2] display(#unused#::PlotlyJS.PlotlyJSDisplay, p::PlotlyJS.SyncPlot)
    @ PlotlyJS C:\Users\beorostica\.julia\packages\PlotlyJS\4jzLr\src\display.jl:160
  [3] display(x::Any)
    @ Base.Multimedia .\multimedia.jl:328
  [4] #invokelatest#2
    @ .\essentials.jl:716 [inlined]
  [5] invokelatest
    @ .\essentials.jl:714 [inlined]
  [6] print_response(errio::IO, response::Any, show_value::Bool, have_color::Bool, specialdisplay::Union{Nothing, AbstractDisplay})
    @ REPL C:\Users\beorostica\AppData\Local\Programs\Julia-1.7.3\share\julia\stdlib\v1.7\REPL\src\REPL.jl:293
  [7] (::REPL.var"#45#46"{REPL.LineEditREPL, Pair{Any, Bool}, Bool, Bool})(io::Any)
    @ REPL C:\Users\beorostica\AppData\Local\Programs\Julia-1.7.3\share\julia\stdlib\v1.7\REPL\src\REPL.jl:277
  [8] with_repl_linfo(f::Any, repl::REPL.LineEditREPL)
    @ REPL C:\Users\beorostica\AppData\Local\Programs\Julia-1.7.3\share\julia\stdlib\v1.7\REPL\src\REPL.jl:510
  [9] print_response(repl::REPL.AbstractREPL, response::Any, show_value::Bool, have_color::Bool)
    @ REPL C:\Users\beorostica\AppData\Local\Programs\Julia-1.7.3\share\julia\stdlib\v1.7\REPL\src\REPL.jl:275
 [10] (::REPL.var"#do_respond#66"{Bool, Bool, REPL.var"#77#87"{REPL.LineEditREPL, REPL.REPLHistoryProvider}, REPL.LineEditREPL, REPL.LineEdit.Prompt})(s::REPL.LineEdit.MIState, buf::Any, ok::Bool)
    @ REPL C:\Users\beorostica\AppData\Local\Programs\Julia-1.7.3\share\julia\stdlib\v1.7\REPL\src\REPL.jl:846
 [11] #invokelatest#2
    @ .\essentials.jl:716 [inlined]
 [12] invokelatest
    @ .\essentials.jl:714 [inlined]
 [13] run_interface(terminal::REPL.Terminals.TextTerminal, m::REPL.LineEdit.ModalInterface, s::REPL.LineEdit.MIState)
    @ REPL.LineEdit C:\Users\beorostica\AppData\Local\Programs\Julia-1.7.3\share\julia\stdlib\v1.7\REPL\src\LineEdit.jl:2493
 [14] run_frontend(repl::REPL.LineEditREPL, backend::REPL.REPLBackendRef)
    @ REPL C:\Users\beorostica\AppData\Local\Programs\Julia-1.7.3\share\julia\stdlib\v1.7\REPL\src\REPL.jl:1232
 [15] (::REPL.var"#49#54"{REPL.LineEditREPL, REPL.REPLBackendRef})()
    @ REPL .\task.jl:429

Things to keep in mind:

  • This is not a problem when the KomaUI() function is called.
  • The function plot_image() that also uses PlotlyJS doesn't have this problem in the Julia REPL.

For the time being, it is possible to visualize the plots by saving them in a file and then open them with a proper program. For example:

julia> p = plot_seq(seq);
julia> savefig(p, "seq.html");

, and then open the seq.html file in your browser.

Default max step-size for RF waveforms

For the time being, the max allowed RF step-size is Δt_rf=5e-5 or $50 \mu\mathrm{s}$. This is relevant to correctly simulate when we have considerable relaxation while the excitation is occurring. Regardless, this has not been thoroughly tested. Maybe $100 \mu\mathrm{s}$ is enough for most cases?

The effect this would have is to accelerate the simulation with simulate(...), without specifying simParams (default parameters).

Code refactoring and efficiency

As it is right now, each call of the SimulationCore's functions allocates the objects in GPU memory.

Changing the functions to use AbstractArrays and allocate GPU memory one time in simulate should be the way to go.

PulseDesigner submodule

Inspired on Pulseq we need to add a submodule with common pulse sequence blocks.

General

PulseDesigner

ℹ️ IMPORTANT

The main difference between these functions and the constructors is the use of the Scanner object. Examples: If $G_\max$ is too low it will make the block longer, Add a delay after the RF blocks to satisfy the ring-down time, etc.

RF

  • makeBlockPulse (Hard pulse, partially implemented)
  • makeSincPulse
  • makeGaussPulse (for fat-sat)
  • makeSLRPulse
  • makeArbitraryRf

Grad

  • makeTrapezoid
  • makeArbitraryGrad
  • makeExtendedTrapezoid
  • makeSpiral?

ADC

  • makeAdc

The submodule could be the same as the one for gradient optimization.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.