This page provides documentation for the modules, structs, functions, methods, and additional components available when importing the KomaMRI package. It serves as a valuable reference when using the Julia REPL directly and when creating custom Julia scripts. Be sure not to overlook the section How to read the API docs, which contains important information for understanding the general structure of docstrings. The following is the table of contents for the API Documentation:
The API documentation includes predefined "template patterns" to assist users in understanding how to use modules, structs, functions, methods, and all the necessary aspects to make the most of what KomaMRI has to offer.
These documentation "template patterns" are based on the JJulia Blue Style documentation and other GitHub repositories that deal with MRI topics. However, some custom considerations were added to enhance understanding and provide a broader perspective.
When you encounter a docstring documentation, it will have the following structure:
This page provides documentation for the modules, structs, functions, methods, and additional components available when importing the KomaMRI package. It serves as a valuable reference when using the Julia REPL directly and when creating custom Julia scripts. Be sure not to overlook the section How to read the API docs, which contains important information for understanding the general structure of docstrings. The following is the table of contents for the API Documentation:
The API documentation includes predefined "template patterns" to assist users in understanding how to use modules, structs, functions, methods, and all the necessary aspects to make the most of what KomaMRI has to offer.
These documentation "template patterns" are based on the JJulia Blue Style documentation and other GitHub repositories that deal with MRI topics. However, some custom considerations were added to enhance understanding and provide a broader perspective.
When you encounter a docstring documentation, it will have the following structure:
The preceding docstring block will always start with the way the component is called (outputs = component_name(inputs), followed by a brief description of what the component does. If necessary, a note block will be displayed. In general, the following subsections are optional: Arguments, Keywords, Returns, References, and Examples, but they will be provided as needed. These subsections are self-explanatory, making it intuitive to understand their purpose.
Please note that every subitem in the sections Arguments, Keywords, and Returns represents variables. They include practical information along with a description. The information enclosed in parentheses is optional but highly useful when provided.
::type: is the suggested type for the variable. If the input variable is of type ::type, there won't be any issues, but it's always possible to test other subtypes. If the variable is an output, it will be forced to the type ::type whenever possible.
=value: sometimes, for the inputs, a default value is defined if it is not assigned by the user.
[unit]: this is the suggested physical unit of measure for the variable. Everything will be fine if you stick with these units of measure.
opts: [opt1, opt2, ...]: sometimes, the input value can only be interpreted if it is one of the predefined values.
return_window: (::Bool, =false) make the out be either 'nothing' or the Blink window, depending on whether the return_window keyword argument is set to true
show_window: (::Bool, =true) display the Blink window
Returns
out: (::Nothing or ::Blink.AtomShell.Window) returns either 'nothing' or the Blink window, depending on whether the return_window keyword argument is set to true.
return_window: (::Bool, =false) make the out be either 'nothing' or the Blink window, depending on whether the return_window keyword argument is set to true
show_window: (::Bool, =true) display the Blink window
Returns
out: (::Nothing or ::Blink.AtomShell.Window) returns either 'nothing' or the Blink window, depending on whether the return_window keyword argument is set to true.
@@ -17,11 +17,11 @@
window.PLOTLYENV = window.PLOTLYENV || {}
- if (document.getElementById('9ad8cdbe-2227-44a7-8fd7-5de25e329b7a')) {
+ if (document.getElementById('de276b70-6328-4ec4-83c9-54f86e192692')) {
Plotly.newPlot(
- '9ad8cdbe-2227-44a7-8fd7-5de25e329b7a',
- [{"y":[null,null,null,null,null],"type":"scatter","name":"Gx","hovertemplate":"(%{x:.4f} ms, %{y:.2f} mT/m)","x":[0.0,0.0,0.0,0.0,0.0]},{"y":[null,null,null,null,null],"type":"scatter","name":"Gy","hovertemplate":"(%{x:.4f} ms, %{y:.2f} mT/m)","x":[0.0,0.0,0.0,0.0,0.0]},{"y":[null,null,null,null,null],"type":"scatter","name":"Gz","hovertemplate":"(%{x:.4f} ms, %{y:.2f} mT/m)","x":[0.0,0.0,0.0,0.0,0.0]},{"y":[null,0.0,0.0,0.0,668.2066312775996,668.2066312775996,1164.3488132933187,1164.3488132933187,1261.3778810677616,1261.3778810677616,850.4448034442171,850.4448034442171,0.0,0.0,1039.4325375429328,1039.4325375429328,1892.0668216016427,1892.0668216016427,2162.3620818304485,2162.3620818304485,1559.1488063143984,1559.1488063143984,0.0,0.0,2338.7232094715982,2338.7232094715982,5045.511524271046,5045.511524271046,7568.267286406571,7568.267286406571,9354.892837886391,9354.892837886391,10000.0,10000.0,9354.892837886391,9354.892837886391,7568.267286406571,7568.267286406571,5045.511524271046,5045.511524271046,2338.7232094715982,2338.7232094715982,0.0,0.0,1559.1488063143984,1559.1488063143984,2162.3620818304485,2162.3620818304485,1892.0668216016427,1892.0668216016427,1039.4325375429328,1039.4325375429328,0.0,0.0,850.4448034442171,850.4448034442171,1261.3778810677616,1261.3778810677616,1164.3488132933187,1164.3488132933187,668.2066312775996,668.2066312775996,0.0,0.0,0.0],"type":"scatter","name":"|RF_1|","hovertemplate":"(%{x:.4f} ms, %{y:.2f} μT)","x":[0.0,0.1,0.1,0.11612903225806452,0.11612903225806452,0.13225806451612904,0.13225806451612904,0.14838709677419357,0.14838709677419357,0.16451612903225807,0.16451612903225807,0.18064516129032257,0.18064516129032257,0.1967741935483871,0.1967741935483871,0.21290322580645163,0.21290322580645163,0.22903225806451616,0.22903225806451616,0.2451612903225807,0.2451612903225807,0.2612903225806452,0.2612903225806452,0.2774193548387097,0.2774193548387097,0.2935483870967743,0.2935483870967743,0.3096774193548388,0.3096774193548388,0.32580645161290334,0.32580645161290334,0.34193548387096784,0.34193548387096784,0.35806451612903234,0.35806451612903234,0.37419354838709684,0.37419354838709684,0.39032258064516134,0.39032258064516134,0.4064516129032259,0.4064516129032259,0.4225806451612904,0.4225806451612904,0.43870967741935496,0.43870967741935496,0.45483870967741946,0.45483870967741946,0.470967741935484,0.470967741935484,0.4870967741935485,0.4870967741935485,0.5032258064516131,0.5032258064516131,0.5193548387096777,0.5193548387096777,0.5354838709677422,0.5354838709677422,0.5516129032258067,0.5516129032258067,0.5677419354838712,0.5677419354838712,0.5838709677419357,0.5838709677419357,0.6000000000000003,0.6000000000000003]},{"y":[null,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-3.141592653589793,-3.141592653589793,-3.141592653589793,-3.141592653589793,-3.141592653589793,-3.141592653589793,-3.141592653589793,-3.141592653589793,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-3.141592653589793,-3.141592653589793,-3.141592653589793,-3.141592653589793,-3.141592653589793,-3.141592653589793,-3.141592653589793,-3.141592653589793,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],"type":"scatter","name":"
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/dev/assets/event-shapes-gr-horizontal.svg b/dev/assets/event-shapes-gr-horizontal.svg
index 399effd65..df03992fd 100644
--- a/dev/assets/event-shapes-gr-horizontal.svg
+++ b/dev/assets/event-shapes-gr-horizontal.svg
@@ -1,4 +1,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/dev/assets/event-shapes-rf-horizontal.svg b/dev/assets/event-shapes-rf-horizontal.svg
index d7d616592..82b761cf3 100644
--- a/dev/assets/event-shapes-rf-horizontal.svg
+++ b/dev/assets/event-shapes-rf-horizontal.svg
@@ -1,4 +1,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/dev/assets/examples/1-seq.html b/dev/assets/examples/1-seq.html
index 85e295ca1..53dca1dce 100644
--- a/dev/assets/examples/1-seq.html
+++ b/dev/assets/examples/1-seq.html
@@ -9,7 +9,7 @@
In this section, we will create a custom Phantom struct. While the example is presented in 2D, the concepts discussed here can be readily extended to 3D phantoms.
In KomaMRI, the creation of a Phantom struct involves defining spin position arrays (x, y, z) and spin property arrays. The indices of these arrays are then associated with independent spins.
For instance, you can create a Phantom with one spin like so:
# Define arrays of positions (spin at zero position)
+Create Your Own Phantom · KomaMRI.jl: General MRI simulation framework
In this section, we will create a custom Phantom struct. While the example is presented in 2D, the concepts discussed here can be readily extended to 3D phantoms.
In KomaMRI, the creation of a Phantom struct involves defining spin position arrays (x, y, z) and spin property arrays. The indices of these arrays are then associated with independent spins.
For instance, you can create a Phantom with one spin like so:
# Define arrays of positions (spin at zero position)
x = [0.0]
y = [0.0]
z = [0.0]
@@ -111,4 +111,4 @@
T2 = T2[ρ.!=0],
T2s = T2s[ρ.!=0],
Δw = Δw[ρ.!=0],
-)
We can display the Phantom struct with the plot_phantom_map function. In this case we select the proton density to be displayed, but you can choose other property to be displayed:
plot_phantom_map(obj, :ρ)
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
+)
We can display the Phantom struct with the plot_phantom_map function. In this case we select the proton density to be displayed, but you can choose other property to be displayed:
plot_phantom_map(obj, :ρ)
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
diff --git a/dev/create-your-own-sequence/index.html b/dev/create-your-own-sequence/index.html
index 2c5ea6e77..20d0fd9ce 100644
--- a/dev/create-your-own-sequence/index.html
+++ b/dev/create-your-own-sequence/index.html
@@ -1,5 +1,5 @@
-Create Your Own Sequence · KomaMRI.jl: General MRI simulation framework
This section is currently under construction, and some details on how to construct a Sequence may be missing.
This is an example of how to create a Sequence struct:
# Export necessary modules
using KomaMRI
# Create the function that creates a phantom
@@ -44,4 +44,4 @@
# Plot the sequence in time and its kspace
plot_seq(seq; range=[0 30])
-plot_kspace(seq)
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
+plot_kspace(seq)
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
y = getproperty(x::Vector{Grad}, f::Symbol)
y = getproperty(x::Matrix{Grad}, f::Symbol)
Overloads Base.getproperty(). It is meant to access properties of the Grad vector x directly without the need to iterate elementwise.
Arguments
x: (::Vector{Grad} or ::Matrix{Grad}) vector or matrix of Grad structs
f: (::Symbol, opts: [:x, :y, :z, :T, :delay, :rise, :delay, :dur, :A, f]) input symbol that represents a property of the vector or matrix of Grad structs
Returns
y: (::Vector{Any} or ::Matrix{Any}) vector or matrix with the property defined by the symbol f for all elements of the Grad vector or matrix x
As we already know, a Sequence struct contains field names that store arrays of RF, Grad and ADC structs. To create a Sequence, it's essential to understand how to create these fundamental events.
In the following subsections, we will provide detailed explanations of event parameters and how to create a Sequence using RF, Grad and ADC events.
As we already know, a Sequence struct contains field names that store arrays of RF, Grad and ADC structs. To create a Sequence, it's essential to understand how to create these fundamental events.
In the following subsections, we will provide detailed explanations of event parameters and how to create a Sequence using RF, Grad and ADC events.
We can include multiple events within a single block of a sequence. The example below demonstrates how to combine an RF struct, three Grad structs for the x-y-z components, and an ADC struct in a single block of a sequence:
# Define an RF struct
+julia> plot_seq(seq; slider=false)
We can include multiple events within a single block of a sequence. The example below demonstrates how to combine an RF struct, three Grad structs for the x-y-z components, and an ADC struct in a single block of a sequence:
Once the struct events are defined, it's important to note that to create a single block sequence, you need to provide 2D matrices of Grad and RF structs, as well as a vector of ADC structs as arguments in the Sequence constructor.
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
+julia> plot_seq(seq; slider=false)
Once the struct events are defined, it's important to note that to create a single block sequence, you need to provide 2D matrices of Grad and RF structs, as well as a vector of ADC structs as arguments in the Sequence constructor.
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
Finally, to simulate we will need to use the function simulate.
raw = simulate(obj, seq, sys)
RawAcquisitionData[SeqName: NoName | 1 Profile(s) of 8192×1]
To plot the results we will need to use the plot_signal function
p2 = plot_signal(raw; slider=false, height=300)
"../../assets/examples/1-signal.html"
Nice!, we can see that $S(t)$ follows an exponential decay $\exp(-t/T_2)$ as expected.
For a little bit of spiciness, let's add off-resonance to our example. We will use $\Delta f=-100\,\mathrm{Hz}$. For this, we will need to add a definition for Δw in our Phantom
obj = Phantom{Float64}(x=[0.], T1=[1000e-3], T2=[100e-3], Δw=[-2π*100])# and simulate again.
raw = simulate(obj, seq, sys)
-p3 = plot_signal(raw; slider=false, height=300)
"../../assets/examples/1-signal2.html"
The signal now follows an exponential of the form $\exp(-t/T_2)\cdot\exp(-i\Delta\omega t)$. The addition of $\exp(-i\Delta\omega t)$ to the signal will generate a shift in the image space (Fourier shifting property). This effect will be better visualized and explained in later examples.
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
+p3 = plot_signal(raw; slider=false, height=300)
"../../assets/examples/1-signal2.html"
The signal now follows an exponential of the form $\exp(-t/T_2)\cdot\exp(-i\Delta\omega t)$. The addition of $\exp(-i\Delta\omega t)$ to the signal will generate a shift in the image space (Fourier shifting property). This effect will be better visualized and explained in later examples.
Based on the results in page 41 of the book "Handbook of MRI Pulse Sequences" by Bernstein et al.
50
In this example, we will showcase a common approximation in MRI, the small tip angle approximation. For this, we will simulate a slice profile for spins with positions $z\in[-2,\,2]\,\mathrm{cm}$ and with a gradient $G_{z}$ so their frequencies are mapped to $f\in[-5,\,5]\,\mathrm{kHz}$. To start, we define an RF pulse with a flip angle of 30 deg and pulse duration of $T_{\mathrm{rf}}=3.2\,\mathrm{ms}$.
B1 = 4.92e-6
+Small Tip Angle Approximation · KomaMRI.jl: General MRI simulation framework
Based on the results in page 41 of the book "Handbook of MRI Pulse Sequences" by Bernstein et al.
50
In this example, we will showcase a common approximation in MRI, the small tip angle approximation. For this, we will simulate a slice profile for spins with positions $z\in[-2,\,2]\,\mathrm{cm}$ and with a gradient $G_{z}$ so their frequencies are mapped to $f\in[-5,\,5]\,\mathrm{kHz}$. To start, we define an RF pulse with a flip angle of 30 deg and pulse duration of $T_{\mathrm{rf}}=3.2\,\mathrm{ms}$.
For this case, the small tip angle approximation breaks 😢, thus, the reason for its name!
This basic sinc pulse is not designed to be $B_{1}$-insensitive. Some adiabatic RF pulses have been proposed to achieve this. Watch out for a future example showing these adiabatic RF pulses 👀.
For this case, the small tip angle approximation breaks 😢, thus, the reason for its name!
This basic sinc pulse is not designed to be $B_{1}$-insensitive. Some adiabatic RF pulses have been proposed to achieve this. Watch out for a future example showing these adiabatic RF pulses 👀.
KomaMRI was written in Julia, so the first thing you should do is to install it! The latest version of Julia can be downloaded at the Julia Downloads page. It is advisable you add julia to the PATH, which can be done during the installation process.
KomaMRI was written in Julia, so the first thing you should do is to install it! The latest version of Julia can be downloaded at the Julia Downloads page. It is advisable you add julia to the PATH, which can be done during the installation process.
For our first simulation we will use KomaMRI's graphical user interface (GUI). For this, you will first need to load KomaMRI by typing using KomaMRI, and then launch the GUI with KomaUI().
julia> using KomaMRI
-julia> KomaUI()
The first time you use this command it may take more time than usual, but a window with the Koma GUI will pop up:
The user interface has some basic definitions for the scanner, phantom, and sequence already preloaded. So you can immediately interact with the simulation and reconstruction processes, and then visualize the results.
As a simple demonstration, press the Simulate! button and wait until the simulation is ready. Now you have acquired the Raw Signal and you should see the following:
Then, press the Reconstruct! button and wait until the reconstruction ends. Now you have reconstructed an Image from the Raw Signal and you should see the following in the GUI:
Congratulations, you successfully simulated an MRI acquisition! 🎊
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
+julia> KomaUI()
The first time you use this command it may take more time than usual, but a window with the Koma GUI will pop up:
The user interface has some basic definitions for the scanner, phantom, and sequence already preloaded. So you can immediately interact with the simulation and reconstruction processes, and then visualize the results.
As a simple demonstration, press the Simulate! button and wait until the simulation is ready. Now you have acquired the Raw Signal and you should see the following:
Then, press the Reconstruct! button and wait until the reconstruction ends. Now you have reconstructed an Image from the Raw Signal and you should see the following in the GUI:
Congratulations, you successfully simulated an MRI acquisition! 🎊
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
KomaMRI is a Julia package meant to simulate general Magnetic Resonance Imaging (MRI) scenarios. Its name comes from the Japanese word for spinning-top こま (ko-ma) as they precess due to gravity like spins in a magnetic field.
KomaMRI generates raw data by solving the Bloch equations using the specified scanner, phantom and sequence. It also provides a Graphical User Interface (GUI) that encapsulates the whole imaging pipeline (simulation and reconstruction).
-
KomaMRI can be used by either:
User Interface: User-friendly interaction. No Julia programming skills are required. Refer to Simulation with User Interface to dive into the details of how to use the GUI.
Scripts : Basic knowledge of Julia is required. Refer to Simulation with Scripts to follow a basic workflow on how to work with KomaMRI.
If you are new to KomaMRI, we recommend starting with the "Getting Started" section to install Julia, KomaMRI, and perform your first simulation.
KomaMRI is a Julia package meant to simulate general Magnetic Resonance Imaging (MRI) scenarios. Its name comes from the Japanese word for spinning-top こま (ko-ma) as they precess due to gravity like spins in a magnetic field.
KomaMRI generates raw data by solving the Bloch equations using the specified scanner, phantom and sequence. It also provides a Graphical User Interface (GUI) that encapsulates the whole imaging pipeline (simulation and reconstruction).
+
KomaMRI can be used in different environments:
User Interface: User-friendly interaction. No Julia programming skills are required. Refer to the User Interface Section to dive into the details of how to use the GUI.
Scripts : Basic knowledge of Julia is required. Refer to the Scripts Section to follow a basic workflow on how to work with KomaMRI.
Notebooks: Basic knowledge of Julia is required. This serves as an alternative development environment featuring user-friendly interactive tools. For guidance on setting up these environments, refer to the Notebooks Section.
If you are new to KomaMRI, we recommend starting with the "Getting Started" section to install Julia, KomaMRI, and perform your first simulation.
This section is meant to be a general overview or summary of the main MRI concepts and insights. It is a good starting point to show up the most relevant components involved and how they are related for raw signal acquisition and image reconstruction. The idea is to have a fresh and clear understanding of what is happening behind the scenes when using the KomaMRI.jl package. Some light background in Differential Equations, Signal Processing and Fourier Theory is advisable to follow along.
In order to generate an image from a phantom object with a scanner system and sequence signals, its necessary to acquire a raw signal$s(t)$. This signal can be though as the sum of all spin magnetizations $M$ of the object:
\[s(t) =
+
MRI Theory · KomaMRI.jl: General MRI simulation framework
This section is meant to be a general overview or summary of the main MRI concepts and insights. It is a good starting point to show up the most relevant components involved and how they are related for raw signal acquisition and image reconstruction. The idea is to have a fresh and clear understanding of what is happening behind the scenes when using the KomaMRI.jl package. Some light background in Differential Equations, Signal Processing and Fourier Theory is advisable to follow along.
In order to generate an image from a phantom object with a scanner system and sequence signals, its necessary to acquire a raw signal$s(t)$. This signal can be though as the sum of all spin magnetizations $M$ of the object:
KomaMRI simulates the magnetization of each spin of a Phantom for variable magnetic fields given by a Sequence. It is assumed that a single spin is independent of the state of the other spins in the system (a key feature that enables parallelization). Furthermore, there are defined two regimes in the Sequence: excitation and precession. During the latter, the excitation fields are nulled and are useful for simplifying some physical equations.
The are more internal considerations in the KomaMRI implementation. The Figure 1 summarizes the functions called to perform the simulation.
+
Simulation · KomaMRI.jl: General MRI simulation framework
KomaMRI simulates the magnetization of each spin of a Phantom for variable magnetic fields given by a Sequence. It is assumed that a single spin is independent of the state of the other spins in the system (a key feature that enables parallelization). Furthermore, there are defined two regimes in the Sequence: excitation and precession. During the latter, the excitation fields are nulled and are useful for simplifying some physical equations.
The are more internal considerations in the KomaMRI implementation. The Figure 1 summarizes the functions called to perform the simulation.
This is another simulation method defined in the source code of KomaMRI. It can be specified by setting the sim_method = BlochDict() entry of the sim_params dictionary.
Note
This section is under construction. More explanation of this simulation method is required.
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
This is another simulation method defined in the source code of KomaMRI. It can be specified by setting the sim_method = BlochDict() entry of the sim_params dictionary.
Note
This section is under construction. More explanation of this simulation method is required.
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
You can use KomaMRI with popular programming environments such as Pluto and Jupyter notebooks. The following sections show how to set up these notebooks and test KomaMRI with them.
You can use KomaMRI with popular programming environments such as Pluto and Jupyter notebooks. The following sections show how to set up these notebooks and test KomaMRI with them.
First, install the Pluto module in your Julia environment. Remember to press the ] button to open the Package Manager Session:
julia>
@(1.9) pkg> add Pluto
Afterward, return to the Julia Session by pressing the backspace button, and then execute the Pluto.run() function:
julia> using Pluto
-julia> Pluto.run()
This should automatically open the Pluto dashboard in your default web browser:
Next, create a new notebook by clicking on + Create a new notebook:
Write and run the following code, which is identical to the Free Induction Decay example. Pluto automatically installs the required modules if they are not present on your system. Additionally, note that we do not use KomaMRI directly since we won't be utilizing the KomaUI() function. Instead, we rely on the KomaMRICore and KomaMRIPlots dependencies. To display plots in Pluto, ensure that you import the PlutoPlots package:"
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
+plot_seq(seq; slider=false, height=300)
This should be sufficient, and now you can start working with KomaMRI using Jupyter notebooks.
If you encounter the issue of WebIO not being detected:
You should already be familiar with the Graphical User Interface of KomaMRI. However, you can also use this package directly from the Julia REPL or write your own Julia scripts. This allows you to unlock the full potential of KomaMRI, enabling you to utilize more of its functionalities and even test your own MRI ideas.
This section demonstrates a basic workflow with KomaMRI through writing your own scripts or entering commands directly into the Julia REPL. Let's begin.
You should already be familiar with the Graphical User Interface of KomaMRI. However, you can also use this package directly from the Julia REPL or write your own Julia scripts. This allows you to unlock the full potential of KomaMRI, enabling you to utilize more of its functionalities and even test your own MRI ideas.
This section demonstrates a basic workflow with KomaMRI through writing your own scripts or entering commands directly into the Julia REPL. Let's begin.
Let's replicate these previous steps in a Julia script. You will obtain the following code, which you can copy and paste into the Julia REPL:
# Import the package
using KomaMRI
# Define scanner, object and sequence
@@ -106,4 +106,4 @@
raw = simulate(obj, seq, sys; sim_params)
# Save the .mat file in the temp directory
-matwrite(joinpath(tempdir(), "koma-raw.mat"), Dict("raw" => raw))
Note that we need to simulate to return an array type (not the default RawAcquisitionData), and then we utilize the matwrite function to save a file named "koma-raw.mat" in your computer's temporary directory. Now, you can navigate to your temporary directory (which you can find by displaying the result of tempdir() in the Julia REPL) and locate the "koma-raw.mat" file.
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
Note that we need to simulate to return an array type (not the default RawAcquisitionData), and then we utilize the matwrite function to save a file named "koma-raw.mat" in your computer's temporary directory. Now, you can navigate to your temporary directory (which you can find by displaying the result of tempdir() in the Julia REPL) and locate the "koma-raw.mat" file.
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
+Search · KomaMRI.jl: General MRI simulation framework
Loading search...
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
diff --git a/dev/search_index.js b/dev/search_index.js
index b2d2706d1..7d6926693 100644
--- a/dev/search_index.js
+++ b/dev/search_index.js
@@ -1,3 +1,3 @@
var documenterSearchIndex = {"docs":
-[{"location":"notebooks/#Notebooks","page":"Notebooks","title":"Notebooks","text":"","category":"section"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"You can use KomaMRI with popular programming environments such as Pluto and Jupyter notebooks. The following sections show how to set up these notebooks and test KomaMRI with them.","category":"page"},{"location":"notebooks/#Using-KomaMRI-with-Pluto","page":"Notebooks","title":"Using KomaMRI with Pluto","text":"","category":"section"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"First, install the Pluto module in your Julia environment. Remember to press the ] button to open the Package Manager Session:\"","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"julia>\n\n@(1.9) pkg> add Pluto","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Afterward, return to the Julia Session by pressing the backspace button, and then execute the Pluto.run() function:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"julia> using Pluto\n\njulia> Pluto.run()","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"This should automatically open the Pluto dashboard in your default web browser:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"
","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Next, create a new notebook by clicking on + Create a new notebook:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"
","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Write and run the following code, which is identical to the Free Induction Decay example. Pluto automatically installs the required modules if they are not present on your system. Additionally, note that we do not use KomaMRI directly since we won't be utilizing the KomaUI() function. Instead, we rely on the KomaMRICore and KomaMRIPlots dependencies. To display plots in Pluto, ensure that you import the PlutoPlots package:\"","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"","category":"page"},{"location":"notebooks/#Using-KomaMRI-with-Jupyter","page":"Notebooks","title":"Using KomaMRI with Jupyter","text":"","category":"section"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Ensure you have Jupyter installed on your computer. Follow this tutorial for installation using Anaconda.","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Next, install the IJulia module in your Julia environment. Remember to press the ] key to open the Package Manager Session:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"julia>\n\n(@v1.9) pkg> add IJulia","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"For this example, make sure to install KomaMRICore and KomaMRIPlots (we do not use KomaMRI directly since we won't be utilizing the KomaUI() function):","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"(@v1.9) pkg> add KomaMRICore\n\n(@v1.9) pkg> add KomaMRIPlots","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Next, open Jupyter, navigate to a working folder, and create a new notebook by clicking on New, then Julia 1.9.3.\"","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"
","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"A new, empty notebook will appear:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"
","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Proceed to write and execute the provided example:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"View code","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"# Import modules\nusing KomaMRICore, KomaMRIPlots\n\n# Define sequence\nampRF = 2e-6 # 2 uT RF amplitude\ndurRF = π / 2 / (2π * γ * ampRF) # required duration for a 90 deg RF pulse\nexc = RF(ampRF, durRF)\n\nnADC = 8192 # number of acquisition samples\ndurADC = 250e-3 # duration of the acquisition\ndelay = 1e-3 # small delay\nacq = ADC(nADC, durADC, delay)\n\nseq = Sequence() # empty sequence\nseq += exc # adding RF-only block\nseq += acq # adding ADC-only block\n\n# Plot the sequence\nplot_seq(seq; slider=false, height=300)","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"
","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"This should be sufficient, and now you can start working with KomaMRI using Jupyter notebooks.","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"If you encounter the issue of WebIO not being detected:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"
","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Refer to this troubleshooting guide for details. Essentially, you need to install a WebIO extension based on your Jupyter installation.","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"EditURL = \"../../../../examples/literate/examples/04-3DSliceSelective.jl\"","category":"page"},{"location":"generated/examples/04-3DSliceSelective/#Slice-Selective-Acquisition-of-3D-Phantom","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"","category":"section"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"(Image: ) (Image: )","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"using KomaMRI # hide\nsys = Scanner() # hide","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"While in the previous examples we simulated using hard RF pulses, in this demonstration we will illustrate the principles of slice selection. First, let's import a 3D phantom, in this case a brain slab (thickness of 2mathrmcm), by calling the function brain_phantom3D.","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"obj = brain_phantom3D()\nobj.Δw .= 0 # Removes the off-resonance\np1 = plot_phantom_map(obj, :T2 ; height=400)\nsavefig(p1, \"../../assets/examples/3-phantom.html\") # hide","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"
","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"Now, we are going to import a sequence which acquires 3 slices in the longitudinal axis. Note that the sequence contains three EPIs to acquire 3 slices of the phantom.","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/epi_multislice.seq\")\nseq = read_seq(seq_file)\np2 = plot_seq(seq; range=[0,10], height=400)\nsavefig(p2, \"../../assets/examples/3-seq.html\") # hide","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"We can take a look to the slice profiles by using the function simulate_slice_profile:","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"z = range(-2., 2., 200) * 1e-2; # -2 to 2 cm\nrf1, rf2, rf3 = findall(is_RF_on.(seq))\nM1 = simulate_slice_profile(seq[rf1]; z)\nM2 = simulate_slice_profile(seq[rf2]; z)\nM3 = simulate_slice_profile(seq[rf3]; z)\n\nusing PlotlyJS # hide\npa = scatter(x=z*1e2, y=abs.(M1.xy), name=\"Slice 1\") # hide\npb = scatter(x=z*1e2, y=abs.(M2.xy), name=\"Slice 2\") # hide\npc = scatter(x=z*1e2, y=abs.(M3.xy), name=\"Slice 3\") # hide\npd = plot([pa,pb,pc], Layout(xaxis=attr(title=\"z [cm]\"), height=300,margin=attr(t=40,l=0,r=0), title=\"Slice profiles for the slice-selective sequence\")) # hide\nsavefig(pd, \"../../assets/examples/3-profile.html\") # hide","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"Now let's simulate the acquisition. Notice the three echoes, one for every slice excitation.","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"raw = simulate(obj, seq, sys; sim_params=Dict{String,Any}(\"Nblocks\"=>20))\np3 = plot_signal(raw; slider=false, height=300)\nsavefig(p3, \"../../assets/examples/3-signal.html\") # hide","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"Finally, we reconstruct the acquiered images.","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"# Get the acquisition data\nacq = AcquisitionData(raw)\n\n# Setting up the reconstruction parameters and perform reconstruction\nNx, Ny = raw.params[\"reconSize\"][1:2]\nreconParams = Dict{Symbol,Any}(:reco=>\"direct\", :reconSize=>(Nx, Ny))\nimage = reconstruction(acq, reconParams)\n\n# Plotting the slices\np4 = plot_image(abs.(image[:, :, 1]); height=360, title=\"Slice 1\")\np5 = plot_image(abs.(image[:, :, 2]); height=360, title=\"Slice 2\")\np6 = plot_image(abs.(image[:, :, 3]); height=360, title=\"Slice 3\")\nsavefig(p4, \"../../assets/examples/3-recon1.html\") # hide\nsavefig(p5, \"../../assets/examples/3-recon2.html\") # hide\nsavefig(p6, \"../../assets/examples/3-recon3.html\") # hide","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"
","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"EditURL = \"../../../../examples/literate/examples/01-FID.jl\"","category":"page"},{"location":"generated/examples/01-FID/#Free-Induction-Decay","page":"Free Induction Decay","title":"Free Induction Decay","text":"","category":"section"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"(Image: ) (Image: )","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"First of all, let's use the KomaMRI package and define the default scanner.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"using KomaMRI\nsys = Scanner() # default hardware definition","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"The free induction decay is the simplest observable NMR signal. This signal is the one that follows a single tipping RF pulse. To recreate this experiment, we will need to define a Sequence with 2 blocks.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"The first block containing an RF pulse with a flip-angle of 90 deg,","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"ampRF = 2e-6 # 2 uT RF amplitude\ndurRF = π / 2 / (2π * γ * ampRF) # required duration for a 90 deg RF pulse\nexc = RF(ampRF,durRF)","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"and the second block containing the ADC.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"nADC = 8192 # number of acquisition samples\ndurADC = 250e-3 # duration of the acquisition\ndelay = 1e-3 # small delay\nacq = ADC(nADC, durADC, delay)","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"Finally, we concatenate the sequence blocks to create the final sequence.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"seq = Sequence() # empty sequence\nseq += exc # adding RF-only block\nseq += acq # adding ADC-only block\np1 = plot_seq(seq; slider=false, height=300)\nsavefig(p1, \"../../assets/examples/1-seq.html\") # hide","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"Now, we will define a Phantom with a single spin at x=0 with T_1=1000mathrmms and T_2=100mathrmms.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"obj = Phantom{Float64}(x=[0.], T1=[1000e-3], T2=[100e-3])","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"Finally, to simulate we will need to use the function simulate.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"raw = simulate(obj, seq, sys)","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"To plot the results we will need to use the plot_signal function","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"p2 = plot_signal(raw; slider=false, height=300)\nsavefig(p2, \"../../assets/examples/1-signal.html\") # hide","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"Nice!, we can see that S(t) follows an exponential decay exp(-tT_2) as expected.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"For a little bit of spiciness, let's add off-resonance to our example. We will use Delta f=-100mathrmHz. For this, we will need to add a definition for Δw in our Phantom","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"obj = Phantom{Float64}(x=[0.], T1=[1000e-3], T2=[100e-3], Δw=[-2π*100])# and simulate again.\n\nraw = simulate(obj, seq, sys)\np3 = plot_signal(raw; slider=false, height=300)\nsavefig(p3, \"../../assets/examples/1-signal2.html\") # hide","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"The signal now follows an exponential of the form exp(-tT_2)cdotexp(-iDeltaomega t). The addition of exp(-iDeltaomega t) to the signal will generate a shift in the image space (Fourier shifting property). This effect will be better visualized and explained in later examples.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"This page was generated using Literate.jl.","category":"page"},{"location":"mri-theory-informal/#MRI-Theory","page":"MRI Theory","title":"MRI Theory","text":"","category":"section"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"This section is meant to be a general overview or summary of the main MRI concepts and insights. It is a good starting point to show up the most relevant components involved and how they are related for raw signal acquisition and image reconstruction. The idea is to have a fresh and clear understanding of what is happening behind the scenes when using the KomaMRI.jl package. Some light background in Differential Equations, Signal Processing and Fourier Theory is advisable to follow along. ","category":"page"},{"location":"mri-theory-informal/#Raw-Signal-Generation","page":"MRI Theory","title":"Raw Signal Generation","text":"","category":"section"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"In order to generate an image from a phantom object with a scanner system and sequence signals, its necessary to acquire a raw signal s(t). This signal can be though as the sum of all spin magnetizations M of the object:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"s(t) =\nint_x int_y int_z\nunderbrace\nM(x y z t)\n_approx alpha image(xyz) \n mathrmdz mathrmdy mathrmdx","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Note that the magnitude of the magnetization is kind of proportional to the image. In real life it's not possible to get directly all the spin magnetizations, however it's possible to obtain the sum of all of them in the raw signal. To avoid losing image information in the sum operation, every spin magnetization resonates with different Larmor frequencies which values depend on the position (xyz) (i.e. modulated or encoded with the basis of the spatial frequency domain). Thus the raw signal can be thought as:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"beginalign tag1\n\ns(t) approx\nint_x int_y int_z\nunderbrace\nm(x y z)\n_alpha image(xyz)\nunderbrace\ne^-j 2 pi k_x(t) x + k_y(t) y + k_z(t) z\n_modulation basis \n mathrmdz mathrmdy mathrmdx\n\nendalign","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"where:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"veck(t) =\nbeginpmatrix\nk_x(t) \nk_y(t) \nk_z(t)\nendpmatrix =\nfrac2pigamma\nbeginpmatrix\nint_0^t G_x(tau) mathrmd tau\nint_0^t G_y(tau) mathrmd tau\nint_0^t G_z(tau) mathrmd tau\nendpmatrix\n \nbeginmatrix*l\ngamma gyromagnetic ratio \nG_i(t) input gradient signals\nendmatrix*","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"In the above expressions, we can see that the frequency of each spin can be manipulated by applying input gradient signals (or a gradient field). In practice, this gradient field is applied in the longitudinal axis hatz (but it is always dependent on the (xyz) position), which makes the spins able to resonate (or precess) at different Larmor frequencies after another input RF pulse excite them in the transverse direction. Both inputs, the gradient signal and the RF pulse, are part of the effective magnetic field vecB(t):","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"vecB(t) = \nbeginpmatrix\nB_1x(t) \nB_1y(t) \nG_x(t) x + G_y(t) y + G_z(t) z\nendpmatrix\n \nbeginmatrix*l\nB_1i(t) input RF pulse (transverse) \nG_i(t) input gradients (longitudinal)\nendmatrix*","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"It's important to highlight that the coil that senses the raw signal can only detects magnetization components oriented in the transverse direction. For this reason is necessary to apply the short RF signal orthogonally to the longitudinal hatz axe.","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"One of the primary concerns, to generate an image is to design proper input signals for the effective magnetic field vecB(t). In particular, by inspecting equation (1), it's possible to manipulate the spacial frequencies k_x(t), k_y(t) and k_z(t) by applying the gradients G_x(t), G_y(t) and G_z(t). Thus, we have information of the raw signal s(t) an the basis e^-j 2 pi k_x(t) x + k_y(t) y + k_z(t) z. Mathematically speaking, every sample of the raw signal is the Fourier transform of the magnetization m(xyz) for a specific point of the spacial frequency domain:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"s(t) = Fourierm (k_x(t) k_y(t) k_z(t))","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Therefore, to get the magnetization m(xyz) for all the points in the spacial domain its necessary to solve the inverse problem with enough points to cover the complete spacial frequency domain, which can be achieved by following a trajectory over time applying different gradient signals (i.e. a trajectory to complete the k-space).","category":"page"},{"location":"mri-theory-informal/#K-Space-and-Acquisition","page":"MRI Theory","title":"K-Space and Acquisition","text":"","category":"section"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Note that the trajectory to cover the k-space eventually can have any continuos shape, however it cannot fill the complete space. Furthermore, due to natural hardware restrictions, the continuos trajectory is sampled during the acquisition of the raw signal st. Thus, every discrete point of st represents a discrete point in the k-space.","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Intuitively, it is desirable to get many points as possible and homogeneously distributed in the k-space. In particular, since the theory behind the raw signal generation is intimately related with the Fourier Transform, a natural way to cover the k-space is by taken a discrete mesh grid of points (trajectories and samples separated by small cubes). In this case, it is possible to apply Fourier theory to define the minimal k-space resolution (separation of the samples in the k-space) to set space dimensions (Field of Views) and prevent aliasing in the image, and define maximal limits in the k-space to set space resolution in the image.","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"underbrace\nDelta k_i\n_k-space resolution\nlongrightarrow\nunderbrace\nFOV_i\n_space width bounds ","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"underbrace\nW_k_i\n_k-space width bounds\nlongrightarrow\nunderbrace\nDelta i\n_space resolution ","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"forall i in x y z","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Even though a mesh grid of discrete points is the natural way of thinking to cover the k-space, it is always possible possible to apply more exotic k-space trajectories, which could be helpful, for instance, to reduce the complete acquisition time. Keep in mind though, this fact must be regarded when solving the inverse problem for obtaining the image, for example by applying and interpolation function before taking the inverse Fourier Transform.","category":"page"},{"location":"mri-theory-informal/#Spin-Dynamics","page":"MRI Theory","title":"Spin Dynamics","text":"","category":"section"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"It's important to point out that all the magnetization spins are independent from each other, so we could separate the phantom object into multiple spins and solve the Bloch Equations for every magnetization vector vecM independently:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"fracmathrmd vecMmathrmd t =\n gamma vecM times vecB\n- fracM_x hatx + M_y hatyT_2\n- fracM_z hatx + M_0 hatyT_1","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"or:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"beginalign tag2\n\nfracmathrmdmathrmdt vecM =\nunderbrace\ngamma\nbeginbmatrix\n 0 B_z -B_y \n-B_z 0 B_x \n B_y -B_x 0\nendbmatrix\nvecM\n_textrotation \n-\nunderbrace\nbeginbmatrix\ntfrac1T_2 0 0 \n0 tfrac1T_2 0 \n0 0 tfrac1T_1\nendbmatrix\nvecM\n_textrelaxation \n+\nunderbrace\nbeginbmatrix\n0 \n0 \ntfracM_0T_1\nendbmatrix\n_textsteady-state \n\nendalign","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"beginmatrix*l\ngamma gyromagnetic ratio \nT_2 transverse relaxation time constant \nT_1 longitudinal relaxation time constant\nendmatrix*","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"vecM(t) =\nbeginpmatrix\nM_x(t) \nM_y(t) \nM_z(t)\nendpmatrix\n \nvecB(t) = \nbeginpmatrix\nB_x(t) \nB_y(t) \nB_z(t)\nendpmatrix =\nbeginpmatrix\nB_1x(t) \nB_1y(t) \nG_x(t) x + G_y(t) y + G_z(t) z\nendpmatrix","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"beginmatrix*l\nB_1i(t) input RF pulse (transverse) \nG_i(t) input gradients (longitudinal)\nendmatrix*","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Note that equation (2) can be separated into three parts:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Rotation: governed by the inputs RF pulse and gradient signals. It gives an initial excitation and the oscillatory behavior for different Larmor frequencies, respectively. \nRelaxation: gives the decay behavior (the magnetization envelope) after the excitation of the spins.\nSteady-State: spins points towards the longitudinal direction after a while.","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Thus, to get the raw signal s(t) it's necessary to solve the Bloch equations (equation (2)) for every spin of the phantom object, then sum up the contributions of all of them and finally consider just the components of the transverse plane:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"s(t) = s_xy(t)\n \ns_xy(t) = s_x(t) + j s_y(t)","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"beginpmatrix\ns_x(t) \ns_y(t) \ns_z(t)\nendpmatrix =\nint_x int_y int_z\nvecM(x y z t)\n mathrmdz mathrmdy mathrmdx","category":"page"},{"location":"docstrings/#API-Documentation","page":"API Documentation","title":"API Documentation","text":"","category":"section"},{"location":"docstrings/#dataflow-graph","page":"API Documentation","title":"Dataflow Graph","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"(Image: )","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Pages = [\"docstrings.md\"]\nDepth = 3","category":"page"},{"location":"docstrings/#datatypes","page":"API Documentation","title":"DataTypes","text":"","category":"section"},{"location":"docstrings/#Mag","page":"API Documentation","title":"Mag","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Mag\nshow(::IO, ::Mag)\ngetproperty(::Vector{Mag}, ::Symbol)","category":"page"},{"location":"docstrings/#KomaMRICore.Mag","page":"API Documentation","title":"KomaMRICore.Mag","text":"mag = Mag(xy::Complex, z::Real)\n\nThe Magnetization struct.\n\nArguments\n\nxy: (::Complex{Float64}) magnetization of a spin in the xy plane\nz: (::Real) magnetization of a spin in the z plane\n\nReturns\n\nmag: (::Mag) Magnetization struct\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#Phantom","page":"API Documentation","title":"Phantom","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.Phantom — Type\nKomaMRI.brain_phantom2D — Function\nKomaMRI.brain_phantom3D — Function","category":"page"},{"location":"docstrings/#Scanner","page":"API Documentation","title":"Scanner","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.Scanner — Type","category":"page"},{"location":"docstrings/#Sequence","page":"API Documentation","title":"Sequence","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.Sequence — Type","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"show(::IO, ::Sequence)\nKomaMRI.is_ADC_on\nKomaMRI.is_RF_on\nKomaMRI.is_GR_on\nKomaMRI.is_Gx_on\nKomaMRI.is_Gy_on\nKomaMRI.is_Gz_on\nKomaMRI.is_Delay\nKomaMRI.durs(::Sequence)\ndur(::Sequence)\nKomaMRI.⏢\nKomaMRI.get_grads\nKomaMRI.get_rfs\nKomaMRI.get_flip_angles\nKomaMRI.get_ADC_on\nKomaMRI.get_kspace\nKomaMRI.get_RF_types\n\nKomaMRI.δ2N","category":"page"},{"location":"docstrings/#Base.show-Tuple{IO, Sequence}","page":"API Documentation","title":"Base.show","text":"str = show(io::IO, s::Sequence)\n\nDisplays information about the Sequence struct s in the julia REPL.\n\nArguments\n\ns: (::Sequence) Sequence struct\n\nReturns\n\nstr (::String) output string message\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#KomaMRICore.is_ADC_on","page":"API Documentation","title":"KomaMRICore.is_ADC_on","text":"y = is_ADC_on(x::Sequence)\ny = is_ADC_on(x::Sequence, t::Union{Array{Float64,1}, Array{Float64,2}})\n\nTells if the sequence seq has elements with ADC active, or active during time t.\n\nArguments\n\nx: (::Sequence) sequence struct\nt: (::Union{Array{Float64,1}, Array{Float64,2}}, [s]) time to check\n\nReturns\n\ny: (::Bool) boolean that tells whether or not the ADC in the sequence is active\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.is_RF_on","page":"API Documentation","title":"KomaMRICore.is_RF_on","text":"y = is_RF_on(x::Sequence)\ny = is_RF_on(x::Sequence, t::Vector{Float64})\n\nTells if the sequence seq has elements with RF active, or active during time t.\n\nArguments\n\nx: (::Sequence) Sequence struct\nt: (::Vector{Float64}, [s]) time to check\n\nReturns\n\ny: (::Bool) boolean that tells whether or not the RF in the sequence is active\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.is_GR_on","page":"API Documentation","title":"KomaMRICore.is_GR_on","text":"y = is_GR_on(x::Sequence)\n\nTells if the sequence seq has elements with GR active.\n\nArguments\n\nx: (::Sequence) Sequence struct\n\nReturns\n\ny: (::Bool) boolean that tells whether or not the GR in the sequence is active\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.dur-Tuple{Sequence}","page":"API Documentation","title":"KomaMRICore.dur","text":"T = dur(x::Sequence)\n\nThe total duration of the sequence in [s].\n\nArguments\n\nx: (::Sequence) Sequence struct\n\nReturns\n\nT: (::Real, [s]) total duration of the sequence\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#KomaMRICore.get_flip_angles","page":"API Documentation","title":"KomaMRICore.get_flip_angles","text":"y = get_flip_angles(x::Sequence)\n\nReturns all the flip angles of the RF pulses in the sequence x.\n\nArguments\n\nx: (::Sequence) Sequence struct\n\nReturns\n\ny: (::Vector{Float64}, [deg]) flip angles\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.get_kspace","page":"API Documentation","title":"KomaMRICore.get_kspace","text":"kspace, kspace_adc = get_kspace(seq::Sequence; Δt=1)\n\nOutputs the designed k-space trajectory of the Sequence seq.\n\nArguments\n\nseq: (::Sequence) Sequence struct\nΔt: (::Real, =1, [s]) nominal delta time separation between two time samples for ADC acquisition and Gradients\n\nReturns\n\nkspace: (3-column ::Matrix{Float64}) kspace\nkspace_adc: (3-column ::Matrix{Float64}) adc kspace\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#Grad","page":"API Documentation","title":"Grad","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.Grad — Type\nKomaMRI.Grad(::Function, ::Real, ::Int64) — Method","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"rotx\nroty\nrotz\nshow(::IO, ::Grad)\ngetproperty(::Vector{Grad}, ::Symbol)\ndur(::Grad)","category":"page"},{"location":"docstrings/#KomaMRICore.rotx","page":"API Documentation","title":"KomaMRICore.rotx","text":"Rx = rotx(θ::Real)\n\nRotates vector counter-clockwise with respect to the x-axis.\n\nArguments\n\nθ: (::Real, [rad]) rotation angle\n\nReturns\n\nRx: (::Matrix{Int64}) rotation matrix\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.roty","page":"API Documentation","title":"KomaMRICore.roty","text":"Ry = roty(θ::Real)\n\nRotates vector counter-clockwise with respect to the y-axis.\n\nArguments\n\nθ: (::Real, [rad]) rotation angle\n\nReturns\n\nRy: (::Matrix{Int64}) rotation matrix\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.rotz","page":"API Documentation","title":"KomaMRICore.rotz","text":"Rz = rotz(θ::Real)\n\nRotates vector counter-clockwise with respect to the z-axis.\n\nArguments\n\nθ: (::Real, [rad]) rotation angle\n\nReturns\n\nRz: (::Matrix{Int64}) rotation matrix\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#Base.show-Tuple{IO, Grad}","page":"API Documentation","title":"Base.show","text":"str = show(io::IO, x::Grad)\n\nDisplays information about the Grad struct x in the julia REPL.\n\nArguments\n\nx: (::Grad) Grad struct\n\nReturns\n\nstr (::String) output string message\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#Base.getproperty-Tuple{Vector{Grad}, Symbol}","page":"API Documentation","title":"Base.getproperty","text":"y = getproperty(x::Vector{Grad}, f::Symbol)\ny = getproperty(x::Matrix{Grad}, f::Symbol)\n\nOverloads Base.getproperty(). It is meant to access properties of the Grad vector x directly without the need to iterate elementwise.\n\nArguments\n\nx: (::Vector{Grad} or ::Matrix{Grad}) vector or matrix of Grad structs\nf: (::Symbol, opts: [:x, :y, :z, :T, :delay, :rise, :delay, :dur, :A, f]) input symbol that represents a property of the vector or matrix of Grad structs\n\nReturns\n\ny: (::Vector{Any} or ::Matrix{Any}) vector or matrix with the property defined by the symbol f for all elements of the Grad vector or matrix x\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#KomaMRICore.dur-Tuple{Grad}","page":"API Documentation","title":"KomaMRICore.dur","text":"y = dur(x::Grad)\ny = dur(x::Vector{Grad})\n\nDuration time in [s] of Grad struct or Grad array. When the input is a gradient vector, then the duration is the maximum duration of all the elements of the gradient vector.\n\nArguments\n\nx: (::Grad or ::Vector{Grad}) RF struct or RF array\n\nReturns\n\ny: (::Float64, [s]) duration of the RF struct or RF array\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#RF","page":"API Documentation","title":"RF","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.RF — Type","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Spinor\nshow(::IO,::Spinor)\n*(::Spinor, ::Spinor)\nRz\nRy\nRx\nKomaMRI.Rg\nKomaMRI.Rφ\nQ\nabs(::Spinor)\n\nshow(::IO, ::RF)\ngetproperty(::Vector{RF}, ::Symbol)\ndur(::RF)\nKomaMRI.RF_fun\nKomaMRI.get_flip_angle\nKomaMRI.get_RF_center","category":"page"},{"location":"docstrings/#KomaMRICore.Spinor","page":"API Documentation","title":"KomaMRICore.Spinor","text":"spinor = Spinor(α, β)\n\nSpinor(α, β) with Cayley-Klein parameters α and β. Based on \"Introduction to the Shinnar-Le Roux algorithm\", Patrick Le Roux (1995). A spinor is a way to represent 3D rotations, the underlying representation is a 2 X 2 complex unitary matrix (alphabetainmathbbC):\n\nR=leftbeginarraycc\nalpha -beta^*\nbeta alpha^*\nendarrayright\n\nwith alpha^2+beta^2 = 1.\n\nThis later operates on the 2times2 representation of (xyz) as follows V^+ = R V R^*.\n\nArguments\n\nα: (::Complex{Float64}) Cayley-Klein parameter α\nβ: (::Complex{Float64}) Cayley-Klein parameter β\n\nReturns\n\nspinor: (::Spinor) Spinor struct\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#Base.show-Tuple{IO, Spinor}","page":"API Documentation","title":"Base.show","text":"str = show(io::IO, s::Spinor)\n\nDisplays the spinor parameters in the julia REPL.\n\nArguments\n\ns: (::Spinor) Spinor struct\n\nReturns\n\nstr: (::String) output string message\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#Base.:*-Tuple{Spinor, Spinor}","page":"API Documentation","title":"Base.:*","text":"s = *(s1::Spinor, s2::Spinor)\n\nSpinor multiplication identity: (α1,β1)×(α2,β2) = (α1 α2 - β2⋆ β1 , β2 α1 + α2⋆ β1)\n\nArguments\n\ns1: (::Spinor) first spinor struct\ns2: (::Spinor) second spinor struct\n\nReturns\n\ns: (::Spinor) multiplication spinnor identity result\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#KomaMRICore.Rz","page":"API Documentation","title":"KomaMRICore.Rz","text":"s = Rz(φ)\n\nSpinor clockwise rotation matrix with angle φ with respect to z-axis.\n\nArguments\n\nφ: (::Real, [rad]) angle with respect to z-axis\n\nReturns\n\ns: (::Spinor) spinnor struct that represents the Rz rotation matrix\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.Ry","page":"API Documentation","title":"KomaMRICore.Ry","text":"s = Ry(θ)\n\nSpinor clockwise rotation matrix with angle θ with respect to y-axis.\n\nArguments\n\nθ: (::Real, [rad]) angle with respect to y-axis\n\nReturns\n\ns: (::Spinor) spinor struct that represents the Ry rotation matrix\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.Rx","page":"API Documentation","title":"KomaMRICore.Rx","text":"s = Rx(θ)\n\nSpinor clockwise rotation matrix with angle θ with respect to x-axis.\n\nArguments\n\nθ: (::Real, [rad]) angle with respect to x-axis\n\nReturns\n\ns: (::Spinor) spinor struct that represents the Rx rotation matrix\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.Q","page":"API Documentation","title":"KomaMRICore.Q","text":"s = Q(φ, nxy, nz)\n\nSpinor rotation matrix. Rotation of φ with respect to the axis of rotation n=(nx, ny, nz).\n\nPauly, J., Le Roux, P., Nishimura, D., & Macovski, A. (1991). Parameter relations for the Shinnar-Le Roux selective excitation pulse design algorithm (NMR imaging). IEEE Transactions on Medical Imaging, 10(1), 53-65. doi:10.1109/42.75611\n\nvarphi=-gammaDelta tsqrtleftB_1right^2+left(boldsymbolGcdotboldsymbolx\nright)^2=-gammaDelta tleftVert boldsymbolBrightVert\n\nboldsymboln=boldsymbolBleftVert boldsymbolBrightVert\n\nArguments\n\nφ: (::Real, [rad]) φ angle\nnxy: (::Real) nxy factor\nnz: (::Real) nz factor\n\nReturns\n\ns: (::Spinor) spinnor struct that represents the Q rotation matrix\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#Base.abs-Tuple{Spinor}","page":"API Documentation","title":"Base.abs","text":"y = abs(s::Spinor)\n\nIt calculates |α|^2 + |β|^2 of the Cayley-Klein parameters.\n\nArguments\n\ns: (::Spinor) spinnor struct\n\nReturns\n\ny: (::Real) result of the abs operator\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#Base.show-Tuple{IO, RF}","page":"API Documentation","title":"Base.show","text":"str = show(io::IO, x::RF)\n\nDisplays information about the RF struct x in the julia REPL.\n\nArguments\n\nx: (::RF) RF struct\n\nReturns\n\nstr: (::String) output string message\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#Base.getproperty-Tuple{Vector{RF}, Symbol}","page":"API Documentation","title":"Base.getproperty","text":"y = getproperty(x::Vector{RF}, f::Symbol)\ny = getproperty(x::Matrix{RF}, f::Symbol)\n\nOverloads Base.getproperty(). It is meant to access properties of the RF vector x directly without the need to iterate elementwise.\n\nArguments\n\nx: (::Vector{RF} or ::Matrix{RF}) vector or matrix of RF structs\nf: (::Symbol, opts: [:A, :Bx, :By, :T, :Δf, :delay and :dur]) input symbol that represents a property of the vector or matrix of RF structs\n\nReturns\n\ny: (::Vector{Any} or ::Matrix{Any}) vector with the property defined by the symbol f for all elements of the RF vector or matrix x\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#KomaMRICore.dur-Tuple{RF}","page":"API Documentation","title":"KomaMRICore.dur","text":"y = dur(x::RF)\ny = dur(x::Array{RF,1})\ny = dur(x::Array{RF,2})\n\nDuration time in [s] of RF struct or RF array.\n\nArguments\n\nx: (::RF or ::Array{RF,1} or ::Array{RF,2}) RF struct or RF array\n\nReturns\n\ny: (::Float64, [s]) duration of the RF struct or RF array\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#ADC","page":"API Documentation","title":"ADC","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.ADC — Type","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"getproperty(::Vector{ADC}, ::Symbol)\nKomaMRI.get_adc_sampling_times\nKomaMRI.get_adc_phase_compensation","category":"page"},{"location":"docstrings/#Base.getproperty-Tuple{Vector{ADC}, Symbol}","page":"API Documentation","title":"Base.getproperty","text":"y = getproperty(x::Vector{ADC}, f::Symbol)\n\nOverloads Base.getproperty(). It is meant to access properties of the ADC vector x directly without the need to iterate elementwise.\n\nArguments\n\nx: (::Vector{ADC}) vector of ADC structs\nf: (::Symbol, opts: [:N, :T, :delay, :Δf, :ϕ, :dur]) input symbol that represents a property of the ADC structs\n\nReturns\n\ny: (::Vector{Any}) vector with the property defined by the f for all elements of the ADC vector x\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#Delay","page":"API Documentation","title":"Delay","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.Delay — Type","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"show(::IO, ::Delay)\n+(::Sequence, ::Delay)","category":"page"},{"location":"docstrings/#Base.show-Tuple{IO, Delay}","page":"API Documentation","title":"Base.show","text":"str = show(io::IO, s::Delay)\n\nDisplays the delay time in m[s] of the delay struct s in the julia REPL.\n\nArguments\n\ns: (::Delay) delay struct\n\nReturns\n\nstr: (::String) output string message\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#Base.:+-Tuple{Sequence, Delay}","page":"API Documentation","title":"Base.:+","text":"seq = +(s::Sequence, d::Delay)\nseq = +(d::Delay, s::Sequence)\n\nAdd a delay to sequence struct. It ultimately affects to the duration of the gradients of a sequence.\n\nArguments\n\ns: (::Sequence) sequence struct\nd: (::Delay) delay struct\n\nReturns\n\nseq: (::Sequence) delayed sequence\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#pulseq","page":"API Documentation","title":"Pulseq.jl","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.read_seq — Function","category":"page"},{"location":"docstrings/#read_Grad","page":"API Documentation","title":"read_Grad","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.read_Grad","category":"page"},{"location":"docstrings/#read_RF","page":"API Documentation","title":"read_RF","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.read_RF","category":"page"},{"location":"docstrings/#read_ADC","page":"API Documentation","title":"read_ADC","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.read_ADC","category":"page"},{"location":"docstrings/#get_block","page":"API Documentation","title":"get_block","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.get_block","category":"page"},{"location":"docstrings/#jemris","page":"API Documentation","title":"JEMRIS.jl","text":"","category":"section"},{"location":"docstrings/#read_phantom_jemris","page":"API Documentation","title":"read_phantom_jemris","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.read_phantom_jemris — Function","category":"page"},{"location":"docstrings/#ismrmrd","page":"API Documentation","title":"ISMRMRD.jl","text":"","category":"section"},{"location":"docstrings/#signal_to_raw_data","page":"API Documentation","title":"signal_to_raw_data","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.signal_to_raw_data — Function","category":"page"},{"location":"docstrings/#pulse-designer","page":"API Documentation","title":"PulseDesigner.jl","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.PulseDesigner — Function\nKomaMRI.PulseDesigner.RF_hard — Function\nKomaMRI.PulseDesigner.EPI — Function\nKomaMRI.PulseDesigner.radial_base — Function","category":"page"},{"location":"docstrings/#key-values-calculation","page":"API Documentation","title":"KeyValuesCalculation.jl","text":"","category":"section"},{"location":"docstrings/#get_theo_A","page":"API Documentation","title":"get_theo_A","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.get_theo_A","category":"page"},{"location":"docstrings/#get_theo_t","page":"API Documentation","title":"get_theo_t","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.get_theo_t","category":"page"},{"location":"docstrings/#get_theo_Gi","page":"API Documentation","title":"get_theo_Gi","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.get_theo_Gi","category":"page"},{"location":"docstrings/#trapezoidal-integration","page":"API Documentation","title":"TrapezoidalIntegration.jl","text":"","category":"section"},{"location":"docstrings/#trapz","page":"API Documentation","title":"trapz","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.trapz","category":"page"},{"location":"docstrings/#cumtrapz","page":"API Documentation","title":"cumtrapz","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.cumtrapz","category":"page"},{"location":"docstrings/#time-step-calculation","page":"API Documentation","title":"TimeStepCalculation.jl","text":"","category":"section"},{"location":"docstrings/#points_from_key_times","page":"API Documentation","title":"points_from_key_times","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.points_from_key_times","category":"page"},{"location":"docstrings/#get_variable_times","page":"API Documentation","title":"get_variable_times","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.get_variable_times","category":"page"},{"location":"docstrings/#get_uniform_times","page":"API Documentation","title":"get_uniform_times","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.get_uniform_times","category":"page"},{"location":"docstrings/#kfoldperm","page":"API Documentation","title":"kfoldperm","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.kfoldperm","category":"page"},{"location":"docstrings/#get_breaks_in_RF_key_points","page":"API Documentation","title":"get_breaks_in_RF_key_points","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.get_breaks_in_RF_key_points","category":"page"},{"location":"docstrings/#simulation-core","page":"API Documentation","title":"SimulationCore.jl","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.simulate — Function\nKomaMRI.simulate_slice_profile — Function","category":"page"},{"location":"docstrings/#print_gpus","page":"API Documentation","title":"print_gpus","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.print_gpus","category":"page"},{"location":"docstrings/#run_spin_precession","page":"API Documentation","title":"run_spin_precession","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.run_spin_precession","category":"page"},{"location":"docstrings/#run_spin_precession_parallel","page":"API Documentation","title":"run_spin_precession_parallel","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.run_spin_precession_parallel","category":"page"},{"location":"docstrings/#run_spin_excitation","page":"API Documentation","title":"run_spin_excitation","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.run_spin_excitation","category":"page"},{"location":"docstrings/#run_spin_excitation_parallel","page":"API Documentation","title":"run_spin_excitation_parallel","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.run_spin_excitation_parallel","category":"page"},{"location":"docstrings/#run_sim_time_iter","page":"API Documentation","title":"run_sim_time_iter","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.run_sim_time_iter","category":"page"},{"location":"docstrings/#display-functions","page":"API Documentation","title":"DisplayFunctions.jl","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.plot_seq — Function\nKomaMRI.plot_image — Function\nplot_kspace — Function\nplot_M0 — Function\nplot_phantom_map — Function\nplot_signal — Function","category":"page"},{"location":"docstrings/#theme_chooser","page":"API Documentation","title":"theme_chooser","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.theme_chooser","category":"page"},{"location":"docstrings/#interp_map","page":"API Documentation","title":"interp_map","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.interp_map","category":"page"},{"location":"docstrings/#plot_dict","page":"API Documentation","title":"plot_dict","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.plot_dict","category":"page"},{"location":"docstrings/#KomaMRIPlots.plot_dict","page":"API Documentation","title":"KomaMRIPlots.plot_dict","text":"str = plot_dict(dict::Dict)\n\nGenerates an HTML table based on the dictionary dict.\n\nArguments\n\ndict: (::Dict) dictionary\n\nReturns\n\nstr: (::String) dictionary as an HTML table\n\n\n\n\n\n","category":"function"},{"location":"getting-started/#Getting-Started","page":"Getting Started","title":"Getting Started","text":"","category":"section"},{"location":"getting-started/#Installing-Julia","page":"Getting Started","title":"Installing Julia","text":"","category":"section"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"KomaMRI was written in Julia, so the first thing you should do is to install it! The latest version of Julia can be downloaded at the Julia Downloads page. It is advisable you add julia to the PATH, which can be done during the installation process.","category":"page"},{"location":"getting-started/#Installing-KomaMRI","page":"Getting Started","title":"Installing KomaMRI","text":"","category":"section"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"Once Julia is installed, open the Julia REPL, and add the KomaMRI package by typing the following commands:","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"Press the ] key and then press enter to bring up Julia's package manager.\nType add KomaMRI and then press enter in the package manager session.","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"This process should take about 5 minutes in a fresh Julia installation. Here is how it looks in the Julia REPL:","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"julia> ]\n\n(@v1.9) pkg> add KomaMRI","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"Then press Ctrl+C or backspace to return to the julia> prompt.","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"","category":"page"},{"location":"getting-started/#My-First-MRI-Simulation","page":"Getting Started","title":"My First MRI Simulation","text":"","category":"section"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"For our first simulation we will use KomaMRI's graphical user interface (GUI). For this, you will first need to load KomaMRI by typing using KomaMRI, and then launch the GUI with KomaUI().","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"julia> using KomaMRI\n\njulia> KomaUI()","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"The first time you use this command it may take more time than usual, but a window with the Koma GUI will pop up:","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"(Image: )","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"The user interface has some basic definitions for the scanner, phantom, and sequence already preloaded. So you can immediately interact with the simulation and reconstruction processes, and then visualize the results.","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"As a simple demonstration, press the Simulate! button and wait until the simulation is ready. Now you have acquired the Raw Signal and you should see the following:","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"(Image: )","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"Then, press the Reconstruct! button and wait until the reconstruction ends. Now you have reconstructed an Image from the Raw Signal and you should see the following in the GUI:","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"(Image: )","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"Congratulations, you successfully simulated an MRI acquisition! 🎊","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"EditURL = \"../../../../examples/literate/examples/02-SmallTipApproximation.jl\"","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/#Small-Tip-Angle-Approximation","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"","category":"section"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"(Image: ) (Image: )","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"Based on the results in page 41 of the book \"Handbook of MRI Pulse Sequences\" by Bernstein et al.","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"using KomaMRI # hide\nsys = Scanner() # hide\nsys.Smax = 50 # hide","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"In this example, we will showcase a common approximation in MRI, the small tip angle approximation. For this, we will simulate a slice profile for spins with positions zin-22mathrmcm and with a gradient G_z so their frequencies are mapped to fin-55mathrmkHz. To start, we define an RF pulse with a flip angle of 30 deg and pulse duration of T_mathrmrf=32mathrmms.","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"B1 = 4.92e-6\nTrf = 3.2e-3\nzmax = 2e-2\nfmax = 5e3\nz = range(-zmax, zmax, 400)\nGz = fmax / (γ * zmax)\nf = γ * Gz * z # hide","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"The designed RF pulse is presented in the figure below, where the additional gradient refocuses the spins' phase after the excitation.","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"seq = PulseDesigner.RF_sinc(B1, Trf, sys; G=[0;0;Gz], TBP=8)\np2 = plot_seq(seq; max_rf_samples=Inf, slider=false)\nsavefig(p2, \"../../assets/examples/42-seq.html\") # hide","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"Now we will perform the simulation using the function simulate_slice_profile. Note that we modified Δt_rf in sim_params to match the resolution of the waveform.","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"sim_params = Dict{String, Any}(\"Δt_rf\" => Trf / length(seq.RF.A[1]))\nM = simulate_slice_profile(seq; z, sim_params)\n\nusing PlotlyJS # hide\ns1 = scatter(x=f, y=real.(M.xy), name=\"Mx\") # hide\ns2 = scatter(x=f, y=imag.(M.xy), name=\"My\") # hide\ndat = seq.RF.A[1] # hide\nN = length(dat) # hide\ndat_pad = [zeros(floor(Int64,N)); dat; zeros(floor(Int64,N))] # hide\nN_pad = length(dat_pad) # hide\nU = 1 / (Trf) * N / N_pad #hide\nu = range(0, (N_pad - 1) * U; step=U) # hide\nu = u .- maximum(u) / 2 .- U/2 # hide\nFT_dat_pad = abs.(KomaMRI.fftc(dat_pad; dims=1)) # hide\nscale_factor = maximum(abs.(M.xy)) / maximum(FT_dat_pad) # hide\ns3 = scatter(x=u, y=FT_dat_pad*scale_factor, name=\"|FT(B₁(t))|\", line=attr(dash=\"dash\")) # hide\npb = plot([s1,s2,s3], Layout(title=\"30 deg SINC pulse (TBP=8, Hamming)\", xaxis_title=\"Frequency [Hz]\", xaxis_range=[-fmax,fmax])) # hide\nsavefig(pb, \"../../assets/examples/4b-profile.html\") # hide","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"This produces the following slice profile:","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"As you can see, for a flip angle of 30 deg, the slice profile is very close to the small tip angle approximation (the Fourier transform of B_1(t)).","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"But what will happen if we use a flip angle of 120 deg instead?","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"α_desired = 120 + 0im # The multiplication of a complex number scales the RF pulse of a Sequence\nα = get_flip_angles(seq)[1] # Previous FA approx 30 deg\nseq = (α_desired / α) * seq # Scaling the pulse to have a flip angle of 120\nM = simulate_slice_profile(seq; z, sim_params)\n\ns1 = scatter(x=f, y=abs.(M.xy), name=\"|Mxy|\") # hide\ndat = seq.RF.A[1] # hide\nN = length(dat) # hide\ndat_pad = [zeros(floor(Int64,N)); dat; zeros(floor(Int64,N))] # hide\nN_pad = length(dat_pad) # hide\nU = 1 / (Trf) * N / N_pad #hide\nu = range(0, (N_pad - 1) * U; step=U) # hide\nu = u .- maximum(u) / 2 .- U/2 # hide\nFT_dat_pad = abs.(KomaMRI.fftc(dat_pad; dims=1)) # hide\nscale_factor = maximum(abs.(M.xy)) / maximum(FT_dat_pad) # hide\ns2 = scatter(x=u, y=FT_dat_pad*scale_factor, name=\"|FT(B₁(t))|\", line=attr(dash=\"dash\")) # hide\npa = plot([s1,s2], Layout(title=\"120 deg SINC pulse (TBP=8, Hamming)\", xaxis_title=\"Frequency [Hz]\", xaxis_range=[-fmax,fmax])) # hide\nsavefig(pa, \"../../assets/examples/4a-profile.html\") # hide","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"For this case, the small tip angle approximation breaks 😢, thus, the reason for its name!","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"This basic sinc pulse is not designed to be B_1-insensitive. Some adiabatic RF pulses have been proposed to achieve this. Watch out for a future example showing these adiabatic RF pulses 👀.","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"This page was generated using Literate.jl.","category":"page"},{"location":"create-your-own-sequence/#Create-Your-Own-Sequence","page":"Create Your Own Sequence","title":"Create Your Own Sequence","text":"","category":"section"},{"location":"create-your-own-sequence/","page":"Create Your Own Sequence","title":"Create Your Own Sequence","text":"warning: Warning\nThis section is currently under construction, and some details on how to construct a Sequence may be missing.","category":"page"},{"location":"create-your-own-sequence/","page":"Create Your Own Sequence","title":"Create Your Own Sequence","text":"This is an example of how to create a Sequence struct:","category":"page"},{"location":"create-your-own-sequence/","page":"Create Your Own Sequence","title":"Create Your Own Sequence","text":"# Export necessary modules\nusing KomaMRI\n\n# Create the function that creates a phantom\nfunction sequence_example(FOV::Real, N::Integer)\n\n # Define initial paramters (TODO: consider when N is even)\n sys = Scanner()\n\tΔt = sys.ADC_Δt\n\tGmax = sys.Gmax\n\tNx = Ny = N #Square acquisition\n\tΔx = FOV/(Nx-1)\n\tTa = Δt*(Nx-1) #4-8 us\n Δτ = Ta/(Ny-1)\n\tGa = 1/(γ*Δt*FOV)\n\tζ = Ga / sys.Smax\n\tGa ≥ sys.Gmax ? error(\"Ga=$(Ga*1e3) mT/m exceeds Gmax=$(Gmax*1e3) mT/m, increase Δt to at least Δt_min=\"\n\t*string(round(1/(γ*Gmax*FOV),digits=2))*\" us.\") : 0\n\tϵ1 = Δτ/(Δτ+ζ)\n\n\t# EPI base\n\tEPI = Sequence(vcat(\n\t [mod(i,2)==0 ? Grad(Ga*(-1)^(i/2),Ta,ζ) : Grad(0.,Δτ,ζ) for i=0:2*Ny-2], #Gx\n\t \t[mod(i,2)==1 ? ϵ1*Grad(Ga,Δτ,ζ) : Grad(0.,Ta,ζ) for i=0:2*Ny-2])) #Gy\n\tEPI.ADC = [mod(i,2)==1 ? ADC(0,Δτ,ζ) : ADC(N,Ta,ζ) for i=0:2*Ny-2]\n\n\t# Pre-wind and wind gradients\n\tϵ2 = Ta/(Ta+ζ)\n PHASE = Sequence(reshape(1/2*[Grad( -Ga, Ta, ζ); ϵ2*Grad(-Ga, Ta, ζ)],:,1)) # This needs to be calculated differently\n\tDEPHASE = Sequence(reshape(1/2*[Grad((-1)^N*Ga, Ta, ζ); ϵ2*Grad(-Ga, Ta, ζ)],:,1)) # for even N\n\tseq = PHASE + EPI + DEPHASE\n\n\t# Saving parameters\n\tseq.DEF = Dict(\"Nx\"=>Nx,\"Ny\"=>Ny,\"Nz\"=>1,\"Name\"=>\"epi\")\n\n # Return the sequence\n\treturn seq\nend\n\n# Call the function to create a sequence\nFOV, N = 23e-2, 101\nseq = sequence_example(FOV, N)\n\n# Plot the sequence in time and its kspace\nplot_seq(seq; range=[0 30])\nplot_kspace(seq)","category":"page"},{"location":"create-your-own-sequence/","page":"Create Your Own Sequence","title":"Create Your Own Sequence","text":"","category":"page"},{"location":"sequence/#Sequence-Definition","page":"Sequence Definition","title":"Sequence Definition","text":"","category":"section"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"This section delves into some details about how a sequence is constructed. The sequence definition in KomaMRI is strongly related to the Pulseq definition. After reading this section, you should be able to create your own Sequence structs for conducting custom simulations using the KomaMRI package.","category":"page"},{"location":"sequence/#Sequence-Overview","page":"Sequence Definition","title":"Sequence Overview","text":"","category":"section"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"Let's introduce the following simple sequence figure to expand from a visual example to a more general sequence definition:","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"A sequence can be thought of as an ordered concatenation of blocks over time. Each block is essentially a sequence with a length of 1. Every block consists of an RF pulse, the (xyz) gradients, and the acquisition of samples. Each block also has an associated time duration. To simplify, we will refer to these components as follows:","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"beginmatrix*l\ntextseqi textblock i of the sequence \ntextseqRFi textRF pulse at the i block \ntextseqGRxi textgradient x at the i block \ntextseqGRyi textgradient y at the i block \ntextseqGRzi textgradient z at the i block \ntextseqADCi textacquisition at the i block \ntextseqDURi textduration at the i block\nendmatrix*","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"The best way to understand the Sequence struct in KomaMRI is by examining the source code where this struct is defined:","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"mutable struct Sequence\n GR::Array{Grad,2}\n RF::Array{RF,2}\n ADC::Array{ADC,1}\n DUR::Array{Any,1}\n DEF::Dict{String,Any}\nend","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"As you can see, a Sequence struct contains 5 field names: ''DEF'' contains information for reconstruction steps (so it is not mandatory to fill it), ''DUR'' is a vector that contains the time durations of each block, ''ADC'' is also a vector with the acquisition samples for every block (an vector of ADC structs), ''GR'' is a 2D matrix which 3 rows representing the x-y-z gradients and columns having the samples of each block (a matrix of Grad structs) and ''RF'' is also a 2D matrix where each row represents a different coil and the columns are for different block samples too (a matrix of RF structs). The RF, Grad and ADC are MRI events that will be explained in the section Events Definitions.","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"warning: Warning\nSo far, KomaMRI can only manage one coil for RF excitations. However, in future versions, parallel transmit pTX will be managed by adding more ``rows'' to the RF matrix of the Sequence field name.","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"In order to understand how a Sequence struct can be manipulated in Julia, let's use the EPI sequence example. You can display basic information of the Sequence variable in the Julia REPL:","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"julia> seq = PulseDesigner.EPI_example()\nSequence[ τ = 62.846 ms | blocks: 204 | ADC: 101 | GR: 205 | RF: 1 | DEF: 5 ]","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"As you can see, this Sequence has 204 blocks, 1 of these blocks has an RF struct with values different from zero, there are 205 number of Grad structs considering the x-y-z components, 101 ADC structs acquire samples of some blocks and 62.846 ms is the total time duration of the complete Sequence.","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"To display the sequence in an graph, we can use the plot_seq function:","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"julia> plot_seq(seq)","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"This way, you can see exactly where the RF, Grad and ADC structs are located in time.","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"You can access and filter information for the RF, Grad, ADC, and DUR field names of a Sequence using the dot notation. This allows you to display helpful information about the organization of the Sequence struct:","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"julia> seq.RF\n1×204 Matrix{RF}:\n ⊓(0.5872 ms) ⇿(0.0 ms) ⇿(0.0 ms) … ⇿(0.0 ms) ⇿(0.0 ms) \n\njulia> seq.GR\n3×204 Matrix{Grad}:\n ⇿(0.5872 ms) ⊓(0.4042 ms) ⊓(0.4042 ms) … ⇿(0.2062 ms) ⊓(0.4042 ms) ⊓(0.4042 ms)\n ⇿(0.5872 ms) ⊓(0.4042 ms) ⇿(0.4042 ms) ⊓(0.2062 ms) ⇿(0.4042 ms) ⊓(0.4042 ms)\n ⇿(0.5872 ms) ⇿(0.0 ms) ⇿(0.0 ms) ⇿(0.0 ms) ⇿(0.0 ms) ⇿(0.0 ms)\n\njulia> seq.ADC\n204-element Vector{ADC}:\n ADC(0, 0.0, 0.0, 0.0, 0.0)\n ADC(0, 0.0, 0.0, 0.0, 0.0)\n ADC(101, 0.00019999999999999998, 0.00010211565434713023, 0.0, 0.0)\n ⋮\n ADC(101, 0.00019999999999999998, 0.00010211565434713023, 0.0, 0.0)\n ADC(0, 0.0, 0.0, 0.0, 0.0)\n\njulia> seq.DUR\n204-element Vector{Float64}:\n 0.0005871650124959989\n 0.0004042313086942605\n 0.0004042313086942605\n ⋮\n 0.0004042313086942605\n 0.0004042313086942605","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"Additionally, you can access a subset of blocks in a Sequence by slicing or indexing. The result will also be a Sequence struct, allowing you to perform the same operations as you would with a full Sequence. For example, if you want to analyze the first 11 blocks, you can do the following:","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"julia> seq[1:11]\nSequence[ τ = 3.837 ms | blocks: 11 | ADC: 5 | GR: 11 | RF: 1 | DEF: 5 ]\n\njulia> seq[1:11].GR\n3×11 Matrix{Grad}:\n ⇿(0.5872 ms) ⊓(0.4042 ms) ⊓(0.4042 ms) … ⊓(0.4042 ms) ⇿(0.2062 ms) ⊓(0.4042 ms)\n ⇿(0.5872 ms) ⊓(0.4042 ms) ⇿(0.4042 ms) ⇿(0.4042 ms) ⊓(0.2062 ms) ⇿(0.4042 ms)\n ⇿(0.5872 ms) ⇿(0.0 ms) ⇿(0.0 ms) ⇿(0.0 ms) ⇿(0.0 ms) ⇿(0.0 ms)\n\njulia> plot_seq(seq[1:11])","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"","category":"page"},{"location":"sequence/#Concatenation-of-Sequences","page":"Sequence Definition","title":"Concatenation of Sequences","text":"","category":"section"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"Sequences can be concatenated side by side. The example below demonstrates how to concatenate sequences:","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"julia> s = PulseDesigner.EPI_example()[1:11]\nSequence[ τ = 3.837 ms | blocks: 11 | ADC: 5 | GR: 11 | RF: 1 | DEF: 5 ]\n\njulia> seq = s + s + s\nSequence[ τ = 11.512 ms | blocks: 33 | ADC: 15 | GR: 33 | RF: 3 | DEF: 5 ]\n\njulia> plot_seq(seq)","category":"page"},{"location":"sequence/","page":"Sequence Definition","title":"Sequence Definition","text":"","category":"page"},{"location":"create-your-own-phantom/#Create-Your-Own-Phantom","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"","category":"section"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"In this section, we will create a custom Phantom struct. While the example is presented in 2D, the concepts discussed here can be readily extended to 3D phantoms.","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"In KomaMRI, the creation of a Phantom struct involves defining spin position arrays (x, y, z) and spin property arrays. The indices of these arrays are then associated with independent spins.","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"For instance, you can create a Phantom with one spin like so:","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"# Define arrays of positions (spin at zero position)\nx = [0.0]\ny = [0.0]\nz = [0.0]\n\n# Define arrays of properties (for CSF tissue)\nρ = [1.0]\nT1 = [2.569]\nT2 = [0.329]\nT2s = [0.058]\n\n# Define the phantom\nspin = Phantom(name=\"spin\", x=x, y=y, z=z, ρ=ρ, T1=T1, T2=T2, T2s=T2s)","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"Phantom{Float64}\n name: String \"spin\"\n x: Array{Float64}((1,)) [0.0]\n y: Array{Float64}((1,)) [0.0]\n z: Array{Float64}((1,)) [0.0]\n ρ: Array{Float64}((1,)) [1.0]\n T1: Array{Float64}((1,)) [2.569]\n T2: Array{Float64}((1,)) [0.329]\n T2s: Array{Float64}((1,)) [0.058]\n Δw: Array{Float64}((1,)) [0.0]\n Dλ1: Array{Float64}((1,)) [0.0]\n Dλ2: Array{Float64}((1,)) [0.0]\n Dθ: Array{Float64}((1,)) [0.0]\n ux: #122 (function of type KomaMRICore.var\"#122#136\")\n uy: #123 (function of type KomaMRICore.var\"#123#137\")\n uz: #124 (function of type KomaMRICore.var\"#124#138\")","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"You can add more properties to the Phantom, such as off-resonance, diffusion parameters, and even functions of motion. However, we won't be utilizing them (except for the off-resonance parameter) to maintain simplicity.","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"If you are familiar with the MRI world, you likely have a 2D or 3D array, where each element contains an ID number identifying a different class of tissue. In this setup, the array axes represent spatial positions, while the elements are used for tissue identification.","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"In this example, we will utilize a .mat file containing arrays with such arrangements. The file is readily available upon installing KomaMRI. Let's read the file and store the 2D data in an array called class:\"","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"# Import necessary modules\nusing KomaMRI, MAT\n\n# Get data from a .mat file\npath_koma = dirname(dirname(pathof(KomaMRI)))\npath_phantom_mat = joinpath(path_koma, \"KomaMRICore\", \"src\", \"datatypes\",\"phantom\", \"brain2D.mat\")\ndata = MAT.matread(path_phantom_mat)\nclass = data[\"axial\"]","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"You can visualize the tissue map using the function plot_image`:","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"plot_image(class)","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"
","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"Let's define the position arrays. You need to know the distance between the spins in the original array (in this case, it is 0.5mm), and then you can determine all the positions like this (the z-component is not calculated since this is a 2D example):","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"# Define spin position arrays\nΔx = .5e-3 # 0.5mm\nM, N = size(class) # Number of spins in x and y\nFOVx = (M-1)*Δx # Field of view in x\nFOVy = (N-1)*Δx # Field of view in y\nx = -FOVx/2:Δx:FOVx/2 # x spin coordinates vector\ny = -FOVy/2:Δx:FOVy/2 # y spin coordinates vector\nx, y = x .+ y'*0, x*0 .+ y' # x and y grid points","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"Now, let's define the arrays for the properties. It's essential to have prior knowledge of the property values for different tissue classes. For example, for muscle tissue, we use ρ = 1, T1 = 900 * 1e-3, T2 = 47 * 1e-3, and T2s = 30 * 1e-3. Additionally, create an array mask to identify the location of a tissue's ID. For muscle with ID = 116, the mask is (class == 116). Finally, to obtain a property, sum all the masks with values for all tissue classes. This process is illustrated below: ","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"# Define the proton density array\nρ = (class.==23)*1 .+ # CSF\n (class.==46)*.86 .+ # GM\n (class.==70)*.77 .+ # WM\n (class.==93)*1 .+ # FAT1\n (class.==116)*1 .+ # MUSCLE\n (class.==139)*.7 .+ # SKIN/MUSCLE\n (class.==162)*0 .+ # SKULL\n (class.==185)*0 .+ # VESSELS\n (class.==209)*.77 .+ # FAT2\n (class.==232)*1 .+ # DURA\n (class.==255)*.77 # MARROW\n\n# Define the T1 decay array\nT1 = (class.==23)*2569 .+ # CSF\n (class.==46)*833 .+ # GM\n (class.==70)*500 .+ # WM\n (class.==93)*350 .+ # FAT1\n (class.==116)*900 .+ # MUSCLE\n (class.==139)*569 .+ # SKIN/MUSCLE\n (class.==162)*0 .+ # SKULL\n (class.==185)*0 .+ # VESSELS\n (class.==209)*500 .+ # FAT2\n (class.==232)*2569 .+ # DURA\n (class.==255)*500 # MARROW\n\n# Define the T2 decay array\nT2 = (class.==23)*329 .+ # CSF\n (class.==46)*83 .+ # GM\n (class.==70)*70 .+ # WM\n (class.==93)*70 .+ # FAT1\n (class.==116)*47 .+ # MUSCLE\n (class.==139)*329 .+ # SKIN/MUSCLE\n (class.==162)*0 .+ # SKULL\n (class.==185)*0 .+ # VESSELS\n (class.==209)*70 .+ # FAT2\n (class.==232)*329 .+ # DURA\n (class.==255)*70 # MARROW\n\n# Define the T2s decay array\nT2s = (class.==23)*58 .+ # CSF\n (class.==46)*69 .+ # GM\n (class.==70)*61 .+ # WM\n (class.==93)*58 .+ # FAT1\n (class.==116)*30 .+ # MUSCLE\n (class.==139)*58 .+ # SKIN/MUSCLE\n (class.==162)*0 .+ # SKULL\n (class.==185)*0 .+ # VESSELS\n (class.==209)*61 .+ # FAT2\n (class.==232)*58 .+ # DURA\n (class.==255)*61 # MARROW\n\n# Define off-resonance array\nΔw_fat = -220*2π\nΔw = (class.==93)*Δw_fat .+ # FAT1\n\t(class.==209)*Δw_fat # FAT2\n\n# Adjust with scaling factor\nT1 = T1*1e-3\nT2 = T2*1e-3\nT2s = T2s*1e-3","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"Finally, we can invoke the Phantom constructor. However, before doing so, we choose not to store spins where the proton density is zero to avoid unnecessary data storage. This is achieved by applying the mask ρ.!=0 to the arrays. Additionally, please note that we set the z-position array filled with zeros, and we interchange the x and y coordinates.\"","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"# Define the phantom\nobj = Phantom{Float64}(\n name = \"custom-brain\",\n\tx = y[ρ.!=0],\n\ty = x[ρ.!=0],\n\tz = 0*x[ρ.!=0],\n\tρ = ρ[ρ.!=0],\n\tT1 = T1[ρ.!=0],\n\tT2 = T2[ρ.!=0],\n\tT2s = T2s[ρ.!=0],\n\tΔw = Δw[ρ.!=0],\n)","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"We can display the Phantom struct with the plot_phantom_map function. In this case we select the proton density to be displayed, but you can choose other property to be displayed:","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"plot_phantom_map(obj, :ρ)","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"","category":"page"},{"location":"api/#API-Documentation","page":"API Documentation","title":"API Documentation","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"This page provides documentation for the modules, structs, functions, methods, and additional components available when importing the KomaMRI package. It serves as a valuable reference when using the Julia REPL directly and when creating custom Julia scripts. Be sure not to overlook the section How to read the API docs, which contains important information for understanding the general structure of docstrings. The following is the table of contents for the API Documentation:","category":"page"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"Pages = [\"api.md\"]\nDepth = 3","category":"page"},{"location":"api/#How-to-read-the-API-docs","page":"API Documentation","title":"How to read the API docs","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"The API documentation includes predefined \"template patterns\" to assist users in understanding how to use modules, structs, functions, methods, and all the necessary aspects to make the most of what KomaMRI has to offer.","category":"page"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"These documentation \"template patterns\" are based on the JJulia Blue Style documentation and other GitHub repositories that deal with MRI topics. However, some custom considerations were added to enhance understanding and provide a broader perspective.","category":"page"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"When you encounter a docstring documentation, it will have the following structure:","category":"page"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"docstring: KomaMRI.component_name — Component\nout1, out2, ... = component_name(arg1, arg2, ...; kw1, kw2, ...)This is a brief description of what component_name does.note: Note\nHere can be placed a note if it is regarded necessary.Argumentsarg1: (::type, =value, [unit], opts: [opt1, opt2, ...]) the description for the arg1\n...Keywordskw1: (::type, =value, [unit], opts: [opt1, opt2, ...]) the description for the kw1\n...Returnsout1: (::type, =value, [unit], opts: [opt1, opt2, ...]) the description for the out1\n...ReferencesSometimes it is a good idea to put some references or links\n...Examplesjulia> arg1, arg2, valkw1, valkw2 = 3.5, \"hello\", 1, true\n\njulia> out1, out2 = component_name(arg1, arg2; kw1=valkw1, kw2=valkw2)","category":"page"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"The preceding docstring block will always start with the way the component is called (outputs = component_name(inputs), followed by a brief description of what the component does. If necessary, a note block will be displayed. In general, the following subsections are optional: Arguments, Keywords, Returns, References, and Examples, but they will be provided as needed. These subsections are self-explanatory, making it intuitive to understand their purpose.","category":"page"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"Please note that every subitem in the sections Arguments, Keywords, and Returns represents variables. They include practical information along with a description. The information enclosed in parentheses is optional but highly useful when provided.","category":"page"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"::type: is the suggested type for the variable. If the input variable is of type ::type, there won't be any issues, but it's always possible to test other subtypes. If the variable is an output, it will be forced to the type ::type whenever possible.\n=value: sometimes, for the inputs, a default value is defined if it is not assigned by the user.\n[unit]: this is the suggested physical unit of measure for the variable. Everything will be fine if you stick with these units of measure.\nopts: [opt1, opt2, ...]: sometimes, the input value can only be interpreted if it is one of the predefined values.","category":"page"},{"location":"api/#Structs","page":"API Documentation","title":"Structs","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"CurrentModule = KomaMRICore","category":"page"},{"location":"api/#Scanner","page":"API Documentation","title":"Scanner","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"Scanner","category":"page"},{"location":"api/#KomaMRICore.Scanner","page":"API Documentation","title":"KomaMRICore.Scanner","text":"sys = Scanner(B0, B1, Gmax, Smax, ADC_Δt, seq_Δt, GR_Δt, RF_Δt,\n RF_ring_down_T, RF_dead_time_T, ADC_dead_time_T)\n\nThe Scanner struct.\n\nArguments\n\nB0: (::Real, =1.5, [T]) main magnetic field strength\nB1: (::Real, =10e-6, [T]) maximum RF amplitude\nGmax: (::Real, =60e-3, [T/m]) maximum gradient amplitude\nSmax: (::Real, =500, [mT/m/ms]) gradient maximum slew-rate\nADC_Δt: (::Real, =2e-6, [s]) ADC raster time\nseq_Δt: (::Real, =1e-5, [s]) sequence-block raster time\nGR_Δt: (::Real, =1e-5, [s]) gradient raster time\nRF_Δt: (::Real, =1e-6, [s]) RF raster time\nRF_ring_down_T: (::Real, =20e-6, [s]) RF ring down time\nRF_dead_time_T: (::Real, =100e-6, [s]) RF dead time\nADC_dead_time_T: (::Real, =10e-6, [s]) ADC dead time\n\nReturns\n\nsys: (::Scanner) Scanner struct\n\nExamples\n\njulia> sys = Scanner()\n\njulia> sys.B0\n\n\n\n\n\n","category":"type"},{"location":"api/#Phantom","page":"API Documentation","title":"Phantom","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"Phantom\nbrain_phantom2D\nbrain_phantom3D","category":"page"},{"location":"api/#KomaMRICore.Phantom","page":"API Documentation","title":"KomaMRICore.Phantom","text":"obj = Phantom(name, x, y, z, ρ, T1, T2, T2s, Δw, Dλ1, Dλ2, Dθ, ux, uy, uz)\n\nThe Phantom struct.\n\nArguments\n\nname: (::String) name of the phantom\nx: (::AbstractVector{T}, [m]) vector of x-positions of the spins\ny: (::AbstractVector{T}, [m]) vector of y-positions of the spins\nz: (::AbstractVector{T}, [m]) vector of z-positions of the spins\nρ: (::AbstractVector{T}) vector of proton density of the spins\nT1: (::AbstractVector{T}, [s]) vector of T1 parameters of the spins\nT2: (::AbstractVector{T}, [s]) vector of T2 parameters of the spins\nT2s: (::AbstractVector{T}, [s]) vector of T2s parameters of the spins\nΔw: (::AbstractVector{T}, [rad/s]) vector of off-resonance parameters of the spins\nDλ1: (::AbstractVector{T}) vector of Dλ1 (diffusion) parameters of the spins\nDλ2: (::AbstractVector{T}) vector of Dλ2 (diffusion) parameters of the spins\nDθ: (::AbstractVector{T}) vector of Dθ (diffusion) parameters of the spins\nux: (::Function) displacement field in the x-axis\nuy: (::Function) displacement field in the y-axis\nuz: (::Function) displacement field in the z-axis\n\nReturns\n\nobj: (::Phantom) Phantom struct\n\nExamples\n\njulia> obj = Phantom()\n\njulia> obj.ρ\n\n\n\n\n\n","category":"type"},{"location":"api/#KomaMRICore.brain_phantom2D","page":"API Documentation","title":"KomaMRICore.brain_phantom2D","text":"phantom = brain_phantom2D(;axis=\"axial\", ss=4)\n\nCreates a two-dimentional brain phantom struct.\n\nReferences\n\nB. Aubert-Broche, D.L. Collins, A.C. Evans: \"A new improved version of the realistic digital brain phantom\" NeuroImage, in review - 2006\nB. Aubert-Broche, M. Griffin, G.B. Pike, A.C. Evans and D.L. Collins: \"20 new digital brain phantoms for creation of validation image data bases\" IEEE TMI, in review - 2006\nhttps://brainweb.bic.mni.mcgill.ca/brainweb\n\nKeywords\n\naxis: (::String, =\"axial\", opts=[\"axial\"]) orientation of the phantom\nss: (::Real, =4) subsampling parameter in all axis\n\nReturns\n\nphantom: (::Phantom) 2D Phantom struct\n\nExamples\n\njulia> obj = brain_phantom2D()\n\njulia> plot_phantom_map(obj, :ρ)\n\n\n\n\n\n","category":"function"},{"location":"api/#KomaMRICore.brain_phantom3D","page":"API Documentation","title":"KomaMRICore.brain_phantom3D","text":"phantom = brain_phantom3D(;ss=4)\n\nCreates a three-dimentional brain phantom struct.\n\nReferences\n\nB. Aubert-Broche, D.L. Collins, A.C. Evans: \"A new improved version of the realistic digital brain phantom\" NeuroImage, in review - 2006\nB. Aubert-Broche, M. Griffin, G.B. Pike, A.C. Evans and D.L. Collins: \"20 new digital brain phantoms for creation of validation image data bases\" IEEE TMI, in review - 2006\nhttps://brainweb.bic.mni.mcgill.ca/brainweb\n\nKeywords\n\nss: (::Real, =4) subsampling parameter in all axis\n\nReturns\n\nphantom: (::Phantom) 3D Phantom struct\n\nExamples\n\njulia> obj = brain_phantom3D()\n\njulia> plot_phantom_map(obj, :ρ)\n\n\n\n\n\n","category":"function"},{"location":"api/#Sequence","page":"API Documentation","title":"Sequence","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"Sequence","category":"page"},{"location":"api/#KomaMRICore.Sequence","page":"API Documentation","title":"KomaMRICore.Sequence","text":"seq = Sequence()\nseq = Sequence(GR)\nseq = Sequence(GR, RF)\nseq = Sequence(GR, RF, ADC)\nseq = Sequence(GR, RF, ADC, DUR)\nseq = Sequence(GR::Array{Grad,1})\nseq = Sequence(GR::Array{Grad,1}, RF::Array{RF,1})\nseq = Sequence(GR::Array{Grad,1}, RF::Array{RF,1}, A::ADC, DUR, DEF)\n\nThe Sequence struct.\n\nArguments\n\nGR: (::Matrix{Grad}) gradient matrix, rows are for (x,y,z) and columns are for time\nRF: (::Matrix{RF}) RF matrix, the 1 row is for the coil and columns are for time\nADC: (::Vector{ADC}) ADC vector in time\nDUR: (::Vector{Float64}, [s]) duration of each sequence-block, this enables delays after RF pulses to satisfy ring-down times\nDEF: (::Dict{String, Any}) dictionary with relevant information of the sequence. The possible keys are [\"AdcRasterTime\", \"GradientRasterTime\", \"Name\", \"Nz\", \"Num_Blocks\", \"Nx\", \"Ny\", \"PulseqVersion\", \"BlockDurationRaster\", \"FileName\", \"RadiofrequencyRasterTime\"]\n\nReturns\n\nseq: (::Sequence) Sequence struct\n\n\n\n\n\n","category":"type"},{"location":"api/#Grad","page":"API Documentation","title":"Grad","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"Grad\nGrad(::Function, ::Real, ::Int64)","category":"page"},{"location":"api/#KomaMRICore.Grad","page":"API Documentation","title":"KomaMRICore.Grad","text":"grad = Grad(A, T)\ngrad = Grad(A, T, rise)\ngrad = Grad(A, T, rise, delay)\ngrad = Grad(A, T, rise, fall, delay)\n\nThe Gradient struct.\n\nArguments\n\nA: (::Float64, [T]) amplitude of the gradient\nT: (::Float64, [s]) duration of the flat-top\nrise: (::Real, [s]) duration of the rise\nfall: (::Real, [s]) duration of the fall\ndelay: (::Real, [s]) duration of the delay\n\nReturns\n\ngrad: (::Grad) gradient struct\n\n\n\n\n\n","category":"type"},{"location":"api/#KomaMRICore.Grad-Tuple{Function, Real, Int64}","page":"API Documentation","title":"KomaMRICore.Grad","text":"grad = Grad(f::Function, T::Real, N::Int64; delay::Real)\n\nGenerates an arbitrary gradient waveform defined by function f in the interval t ∈ [0,T]. It uses N square gradients uniformly spaced in the interval.\n\nArguments\n\nf: (::Function) function that describes the gradient waveform\nT: (::Real, [s]) duration of the gradient waveform\nN: (::Int64) number of samples of the gradient waveform\n\nKeywords\n\ndelay: (::Real, =0, [s]) starting delay for the waveform\n\nReturns\n\ngrad: (::Grad) gradient struct\n\nExamples\n\njulia> f1 = t -> sin(π*t / 0.8)\n\njulia> seq = Sequence([Grad(f1, 0.8)])\n\njulia> plot_seq(seq)\n\n\n\n\n\n","category":"method"},{"location":"api/#RF","page":"API Documentation","title":"RF","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"RF","category":"page"},{"location":"api/#KomaMRICore.RF","page":"API Documentation","title":"KomaMRICore.RF","text":"rf = RF(A, T)\nrf = RF(A, T, Δf)\nrf = RF(A, T, Δf, delay)\n\nThe RF struct.\n\nArguments\n\nA: (::Complex{Int64}, [T]) the amplitud-phase B1x + i B1y\nT: (::Int64, [s]) the duration of the RF\nΔf: (::Float64, [Hz]) the frequency offset of the RF\ndelay: (::Float64, [s]) the delay time of the RF\n\nReturns\n\nrf: (::RF) the RF struct\n\n\n\n\n\n","category":"type"},{"location":"api/#ADC","page":"API Documentation","title":"ADC","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"ADC","category":"page"},{"location":"api/#KomaMRICore.ADC","page":"API Documentation","title":"KomaMRICore.ADC","text":"adc = ADC(N, T)\nadc = ADC(N, T, delay)\nadc = ADC(N, T, delay, Δf, ϕ)\n\nThe ADC struct.\n\nArguments\n\nN: (::Int64) number of acquired samples\nT: (::Float64, [s]) duration to acquire the samples\ndelay: (::Float64, [s]) delay time to start the acquisition\nΔf: (::Float64, [Hz]) delta frequency. It's meant to compensate RF pulse phases. It is used internally by the read_ADC function\nϕ: (::Float64, [rad]) phase. It's meant to compensate RF pulse phases. It is used internally by the read_ADC function\n\nReturns\n\nadc: (::ADC) ADC struct\n\n\n\n\n\n","category":"type"},{"location":"api/#Delay","page":"API Documentation","title":"Delay","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"Delay","category":"page"},{"location":"api/#KomaMRICore.Delay","page":"API Documentation","title":"KomaMRICore.Delay","text":"delay = Delay(T)\n\nThe Delay struct. It is a special \"object\" meant to add a delay to a sequence by using a sum operator.\n\nArguments\n\nT: (::Real, [s]) time delay value\n\nReturns\n\ndelay: (::Delay) delay struct\n\n\n\n\n\n","category":"type"},{"location":"api/#Read-Data","page":"API Documentation","title":"Read Data","text":"","category":"section"},{"location":"api/#read_seq","page":"API Documentation","title":"read_seq","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"read_seq","category":"page"},{"location":"api/#KomaMRICore.read_seq","page":"API Documentation","title":"KomaMRICore.read_seq","text":"seq = read_seq(filename)\n\nReturns the Sequence struct from a sequence file .seq.\n\nArguments\n\nfilename: (::String) the absolute or relative path of the sequence file .seq\n\nReturns\n\nseq: (::Sequence) Sequence struct\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_seq(seq)\n\n\n\n\n\n","category":"function"},{"location":"api/#read_phantom_jemris","page":"API Documentation","title":"read_phantom_jemris","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"read_phantom_jemris","category":"page"},{"location":"api/#KomaMRICore.read_phantom_jemris","page":"API Documentation","title":"KomaMRICore.read_phantom_jemris","text":"phantom = read_phantom_jemris(filename)\n\nReturns the Phantom struct from a JEMRIS phantom file .h5.\n\nArguments\n\nfilename: (::String) the absolute or relative path of the phantom file .h5\n\nReturns\n\nphantom: (::Phantom) Phantom struct\n\nExamples\n\njulia> obj_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/2.phantoms/brain.h5\")\n\njulia> obj = read_phantom_jemris(obj_file)\n\njulia> plot_phantom_map(obj, :ρ)\n\n\n\n\n\n","category":"function"},{"location":"api/#read_phantom_MRiLab","page":"API Documentation","title":"read_phantom_MRiLab","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"read_phantom_MRiLab","category":"page"},{"location":"api/#KomaMRICore.read_phantom_MRiLab","page":"API Documentation","title":"KomaMRICore.read_phantom_MRiLab","text":"phantom = read_phantom_MRiLab(filename)\n\nReturns the Phantom struct from a MRiLab phantom file .mat.\n\nArguments\n\nfilename: (::String) the absolute or relative path of the phantom file .mat\n\nReturns\n\nphantom: (::Phantom) Phantom struct\n\nExamples\n\njulia> obj_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/2.phantoms/brain.mat\")\n\njulia> obj = read_phantom_MRiLab(obj_file)\n\njulia> plot_phantom_map(obj, :ρ)\n\n\n\n\n\n","category":"function"},{"location":"api/#signal_to_raw_data","page":"API Documentation","title":"signal_to_raw_data","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"signal_to_raw_data","category":"page"},{"location":"api/#KomaMRICore.signal_to_raw_data","page":"API Documentation","title":"KomaMRICore.signal_to_raw_data","text":"raw_ismrmrd = signal_to_raw_data(signal, seq; phantom_name, sys, simParams)\n\nTransforms the raw signal into ISMRMRD format.\n\nArguments\n\nsignal: (::Vector{ComplexF64}) raw signal\nseq: (::Sequence) Sequence struct\n\nKeywords\n\nphantom_name: (::String, =\"Phantom\") Phantom struct\nsys: (::Scanner, =Scanner()) Scanner struct\nsimParams: (::Dict{String,Any}(), =Dict{String,Any}()) dictionary with simulation parameters\n\nReturns\n\nraw_ismrmrd: (::RawAcquisitionData) raw signal in ISMRMRD format\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/3.koma_paper/comparison_accuracy/sequences/EPI/epi_100x100_TE100_FOV230.seq\");\n\njulia> sys, obj, seq = Scanner(), brain_phantom2D(), read_seq(seq_file)\n\njulia> raw = simulate(obj, seq, sys)\n\njulia> plot_signal(raw)\n\n\n\n\n\n","category":"function"},{"location":"api/#Pulse-Design","page":"API Documentation","title":"Pulse Design","text":"","category":"section"},{"location":"api/#PulseDesigner","page":"API Documentation","title":"PulseDesigner","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"PulseDesigner","category":"page"},{"location":"api/#KomaMRICore.PulseDesigner","page":"API Documentation","title":"KomaMRICore.PulseDesigner","text":"PulseDesigner\n\nA module to define different pulse sequences.\n\n\n\n\n\n","category":"module"},{"location":"api/#PulseDesigner.RF_hard","page":"API Documentation","title":"PulseDesigner.RF_hard","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"PulseDesigner.RF_hard","category":"page"},{"location":"api/#KomaMRICore.PulseDesigner.RF_hard","page":"API Documentation","title":"KomaMRICore.PulseDesigner.RF_hard","text":"ex = RF_hard(B1, T, sys::Scanner; G=[0,0,0], Δf=0)\n\nDefinition of the RF hard sequence.\n\nArguments\n\nB1: (Float64, [T]) amplitude of the RF pulse\nT: (Float64, [s]) duration of the RF pulse\nsys: (::Scanner) Scanner struct\n\nKeywords\n\nG: (Vector{Float64}, =[0, 0, 0], [T]) gradient amplitudes for x, y, z\nΔf: (Float64, =0, [Hz]) frequency offset of the RF pulse\n\nReturns\n\nex: (::Sequence) excitation Sequence struct\n\nExamples\n\njulia> sys = Scanner();\n\njulia> durRF = π/2/(2π*γ*sys.B1); #90-degree hard excitation pulse\n\njulia> ex = PulseDesigner.RF_hard(sys.B1, durRF, sys)\nSequence[ τ = 0.587 ms | blocks: 1 | ADC: 0 | GR: 0 | RF: 1 | DEF: 0 ]\n\njulia> plot_seq(ex)\n\n\n\n\n\n","category":"function"},{"location":"api/#PulseDesigner.RF_sinc","page":"API Documentation","title":"PulseDesigner.RF_sinc","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"PulseDesigner.RF_sinc","category":"page"},{"location":"api/#KomaMRICore.PulseDesigner.RF_sinc","page":"API Documentation","title":"KomaMRICore.PulseDesigner.RF_sinc","text":"seq = spiral_base(FOV::Float64, Nr::Int, sys::Scanner)\n\nDefinition of the radial base sequence.\n\nArguments\n\nFOV: (::Float64, [m]) field of view\nN: (::Int) number of pixels along the radious\nsys: (::Scanner) Scanner struct\n\nReturns\n\nex: (::Sequence) RF struct\n\nReferences\n\nMATT A. BERNSTEIN, KEVIN F. KING, XIAOHONG JOE ZHOU, CHAPTER 2 - RADIOFREQUENCY PULSE SHAPES, Handbook of MRI Pulse Sequences, 2004, Pages 35-66, https://doi.org/10.1016/B978-012092861-3/50006-6.\n\n\n\n\n\n","category":"function"},{"location":"api/#PulseDesigner.EPI","page":"API Documentation","title":"PulseDesigner.EPI","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"PulseDesigner.EPI","category":"page"},{"location":"api/#KomaMRICore.PulseDesigner.EPI","page":"API Documentation","title":"KomaMRICore.PulseDesigner.EPI","text":"epi = EPI(FOV::Float64, N::Int, sys::Scanner)\n\nDefinition of the EPI sequence.\n\nArguments\n\nFOV: (::Float64, [m]) field of view\nN: (::Int) number of pixels in the x and y axis\nsys: (::Scanner) Scanner struct\n\nReturns\n\nepi: (::Sequence) epi Sequence struct\n\nExamples\n\njulia> sys, FOV, N = Scanner(), 23e-2, 101\n\njulia> epi = PulseDesigner.EPI(FOV, N, sys)\nSequence[ τ = 62.259 ms | blocks: 203 | ADC: 101 | GR: 205 | RF: 0 | DEF: 4 ]\n\njulia> plot_seq(epi)\n\n\n\n\n\n","category":"function"},{"location":"api/#PulseDesigner.radial_base","page":"API Documentation","title":"PulseDesigner.radial_base","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"PulseDesigner.radial_base","category":"page"},{"location":"api/#KomaMRICore.PulseDesigner.radial_base","page":"API Documentation","title":"KomaMRICore.PulseDesigner.radial_base","text":"seq = radial_base(FOV::Float64, Nr::Int, sys::Scanner)\n\nDefinition of the radial base sequence.\n\nArguments\n\nFOV: (::Float64, [m]) field of view\nN: (::Int) number of pixels along the radious\nsys: (::Scanner) Scanner struct\n\nReturns\n\nseq: (::Sequence) radial base Sequence struct\n\n\n\n\n\n","category":"function"},{"location":"api/#PulseDesigner.spiral_base","page":"API Documentation","title":"PulseDesigner.spiral_base","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"PulseDesigner.spiral_base","category":"page"},{"location":"api/#KomaMRICore.PulseDesigner.spiral_base","page":"API Documentation","title":"KomaMRICore.PulseDesigner.spiral_base","text":"seq = spiral_base(FOV::Float64, Nr::Int, sys::Scanner)\n\nDefinition of the radial base sequence.\n\nArguments\n\nFOV: (::Float64, [m]) field of view\nN: (::Int) number of pixels along the radious\nsys: (::Scanner) Scanner struct\n\nReturns\n\nseq: (::Function) function that returns a Sequence when evaluated\n\nReferences\n\nGlover, G.H. (1999), Simple analytic spiral K-space algorithm. Magn. Reson. Med., 42: 412-415. https://doi.org/10.1002/(SICI)1522-2594(199908)42:2<412::AID-MRM25>3.0.CO;2-U\n\n\n\n\n\n","category":"function"},{"location":"api/#PulseDesigner.EPI_example","page":"API Documentation","title":"PulseDesigner.EPI_example","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"PulseDesigner.EPI_example","category":"page"},{"location":"api/#Simulation","page":"API Documentation","title":"Simulation","text":"","category":"section"},{"location":"api/#simulate","page":"API Documentation","title":"simulate","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"simulate","category":"page"},{"location":"api/#KomaMRICore.simulate","page":"API Documentation","title":"KomaMRICore.simulate","text":"out = simulate(obj::Phantom, seq::Sequence, sys::Scanner; simParams, w)\n\nReturns the raw signal or the last state of the magnetization according to the value of the \"return_type\" key of the simParams dictionary.\n\nArguments\n\nobj: (::Phantom) Phantom struct\nseq: (::Sequence) Sequence struct\nsys: (::Scanner) Scanner struct\n\nKeywords\n\nsimParams: (::Dict{String,Any}, =Dict{String,Any}()) the dictionary with simulation parameters\nw: (::Any, =nothing) the flag to regard a progress bar in the blink window UI. If this variable is differnet from nothing, then the progress bar is considered\n\nReturns\n\nout: (::Vector{ComplexF64} or ::S <: SpinStateRepresentation or RawAcquisitionData) depending if \"return_type\" is \"mat\" or \"state\" or \"raw\" (default) respectively.\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/3.koma_paper/comparison_accuracy/sequences/EPI/epi_100x100_TE100_FOV230.seq\");\n\njulia> sys, obj, seq = Scanner(), brain_phantom2D(), read_seq(seq_file)\n\njulia> raw = simulate(obj, seq, sys)\n\njulia> plot_signal(raw)\n\n\n\n\n\n","category":"function"},{"location":"api/#simulate_slice_profile","page":"API Documentation","title":"simulate_slice_profile","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"simulate_slice_profile","category":"page"},{"location":"api/#KomaMRICore.simulate_slice_profile","page":"API Documentation","title":"KomaMRICore.simulate_slice_profile","text":"M = simulate_slice_profile(seq; z, simParams)\n\nReturns magnetization of spins distributed along z after running the Sequence seq.\n\nArguments\n\nseq: (::Sequence) Sequence struct\n\nKeywords\n\nz: (=range(-2e-2,2e-2,200)) range for the z axis\nsimParams: (::Dict{String, Any}, =Dict{String,Any}(\"Δt_rf\"=>1e-6)) dictionary with simulation parameters\n\nReturns\n\nM: (::Vector{Mag}) final state of the Mag vector\n\n\n\n\n\n","category":"function"},{"location":"api/#Plots","page":"API Documentation","title":"Plots","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"CurrentModule = KomaMRIPlots","category":"page"},{"location":"api/#plot_phantom_map","page":"API Documentation","title":"plot_phantom_map","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_phantom_map","category":"page"},{"location":"api/#KomaMRIPlots.plot_phantom_map","page":"API Documentation","title":"KomaMRIPlots.plot_phantom_map","text":"p = plot_phantom_map(ph, key; t0=0, height=600, width=nothing, darkmode=false)\n\nPlots a phantom map for a specific spin parameter given by key.\n\nArguments\n\nph: (::Phantom) Phantom struct\nkey: (::Symbol, opts: [:ρ, :T1, :T2, :T2s, :x, :y, :z]) symbol for displaying different parameters of the phantom spins\n\nKeywords\n\nt0: (::Float64, =0, [ms]) time to see displacement of the phantom\nheight: (::Int64, =600) height of the plot\nwidth: (::Int64, =nothing) width of the plot\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nview_2d: (::Bool, =false) boolean to use a 2D scatter plot\ncolorbar: (::Bool, =true) boolean to show the colorbar\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the phantom map for a specific spin parameter\n\nReferences\n\nColormaps from https://github.com/markgriswold/MRFColormaps Towards Unified Colormaps for Quantitative MRF Data, Mark Griswold, et al. (2018).\n\nExamples\n\njulia> obj2D, obj3D = brain_phantom2D(), brain_phantom3D();\n\njulia> plot_phantom_map(obj2D, :ρ)\n\njulia> plot_phantom_map(obj3D, :ρ)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_seq","page":"API Documentation","title":"plot_seq","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_seq","category":"page"},{"location":"api/#KomaMRIPlots.plot_seq","page":"API Documentation","title":"KomaMRIPlots.plot_seq","text":"p = plot_seq(seq; width, height, slider, show_seq_blocks, show_sim_blocks, Nblocks,\n darkmode, max_rf_samples, range)\n\nPlots a sequence struct.\n\nArguments\n\nseq: (::Sequence) Sequence struct\n\nKeywords\n\nwidth: (::Int64, =nothing) width of the plot\nheight: (::Int64, =nothing) height of the plot\nslider: (::Bool, =true) boolean to display a slider\nshow_seq_blocks: (::Bool, =false) boolean to show sequence blocks\nshow_sim_blocks: (::Bool, =false) boolean to show simulation blocks\nNblocks: (::Int64, =0) number of simulation blocks to display\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nmax_rf_samples: (::Int64, =100) maximum number of RF samples\nrange: (::Vector{Float64}, =[]) time range to be displayed initially\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the Sequence struct\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_seq(seq)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_kspace","page":"API Documentation","title":"plot_kspace","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_kspace","category":"page"},{"location":"api/#KomaMRIPlots.plot_kspace","page":"API Documentation","title":"KomaMRIPlots.plot_kspace","text":"p = plot_kspace(seq; width=nothing, height=nothing, darkmode=false)\n\nPlots the k-space of a sequence struct.\n\nArguments\n\nseq: (::Sequence) Sequence struct\n\nKeywords\n\nwidth: (::Int64, =nothing) width of the plot\nheight: (::Int64, =nothing) height of the plot\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the k-space of the sequence struct seq\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_kspace(seq)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_M0","page":"API Documentation","title":"plot_M0","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_M0","category":"page"},{"location":"api/#KomaMRIPlots.plot_M0","page":"API Documentation","title":"KomaMRIPlots.plot_M0","text":"p = plot_M0(seq; height=nothing, width=nothing, slider=true, darkmode=false, range=[])\n\nPlots the zero order moment (M0) of a Sequence seq.\n\nArguments\n\nseq: (::Sequence) Sequence struct\n\nKeywords\n\nheight: (::Int64, =nothing) height of the plot\nwidth: (::Int64, =nothing) width of the plot\nslider: (::Bool, =true) boolean to display a slider\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nrange: (::Vector{Float64}, =[]) time range to be displayed initially\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the moment M0 of the sequence struct seq\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_M0(seq)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_M1","page":"API Documentation","title":"plot_M1","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_M1","category":"page"},{"location":"api/#KomaMRIPlots.plot_M1","page":"API Documentation","title":"KomaMRIPlots.plot_M1","text":"p = plot_M1(seq; height=nothing, width=nothing, slider=true, darkmode=false, range=[])\n\nPlots the first order moment (M1) of a Sequence seq.\n\nArguments\n\nseq: (::Sequence) Sequence\n\nKeywords\n\nheight: (::Int64, =nothing) height of the plot\nwidth: (::Int64, =nothing) width of the plot\nslider: (::Bool, =true) boolean to display a slider\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nrange: (::Vector{Float64}, =[]) time range to be displayed initially\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the moment M1 of the sequence struct seq\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_M1(seq)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_M2","page":"API Documentation","title":"plot_M2","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_M2","category":"page"},{"location":"api/#KomaMRIPlots.plot_M2","page":"API Documentation","title":"KomaMRIPlots.plot_M2","text":"p = plot_M2(seq; height=nothing, width=nothing, slider=true, darkmode=false, range=[])\n\nPlots the second order moment (M2) of a Sequence seq.\n\nArguments\n\nseq: (::Sequence) Sequence\n\nKeywords\n\nheight: (::Int64, =nothing) height of the plot\nwidth: (::Int64, =nothing) width of the plot\nslider: (::Bool, =true) boolean to display a slider\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nrange: (::Vector{Float64}, =[]) time range to be displayed initially\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the moment M2 of the sequence struct seq\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_M2(seq)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_eddy_currents","page":"API Documentation","title":"plot_eddy_currents","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_eddy_currents","category":"page"},{"location":"api/#KomaMRIPlots.plot_eddy_currents","page":"API Documentation","title":"KomaMRIPlots.plot_eddy_currents","text":"p = plot_eddy_currents(seq, λ; α=ones(size(λ)), height=nothing, width=nothing, slider=true, darkmode=false, range=[])\n\nPlots the eddy currents of a Sequence seq.\n\nArguments\n\nseq: (::Sequence) Sequence\nλ: (::Float64, [s]) eddy currents decay constant time\n\nKeywords\n\nα: (::Vector{Float64}, =ones(size(λ))) eddy currents factors\nheight: (::Int64, =nothing) height of the plot\nwidth: (::Int64, =nothing) width of the plot\nslider: (::Bool, =true) boolean to display a slider\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nrange: (::Vector{Float64}, =[]) time range to be displayed initially\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the eddy currents of the sequence struct seq\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_eddy_currents(seq, 80e-3)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_slew_rate","page":"API Documentation","title":"plot_slew_rate","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_slew_rate","category":"page"},{"location":"api/#KomaMRIPlots.plot_slew_rate","page":"API Documentation","title":"KomaMRIPlots.plot_slew_rate","text":"p = plot_slew_rate(seq; height=nothing, width=nothing, slider=true, darkmode=false, range=[])\n\nPlots the slew rate currents of a Sequence seq.\n\nArguments\n\nseq: (::Sequence) Sequence\n\nKeywords\n\nheight: (::Int64, =nothing) height of the plot\nwidth: (::Int64, =nothing) width of the plot\nslider: (::Bool, =true) boolean to display a slider\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nrange: (::Vector{Float64}, =[]) time range to be displayed initially\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the slew rate currents of the sequence struct seq\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_slew_rate(seq)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_signal","page":"API Documentation","title":"plot_signal","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_signal","category":"page"},{"location":"api/#KomaMRIPlots.plot_signal","page":"API Documentation","title":"KomaMRIPlots.plot_signal","text":"p = plot_signal(raw::RawAcquisitionData; height, width, slider, show_sim_blocks,\n darkmode, range)\n\nPlots a raw signal in ISMRMRD format.\n\nArguments\n\nraw: (::RawAcquisitionData) RawAcquisitionData struct which is the raw signal in ISMRMRD format\n\nKeywords\n\nwidth: (::Int64, =nothing) width of the plot\nheight: (::Int64, =nothing) height of the plot\nslider: (::Bool, =true) boolean to display a slider\nshow_sim_blocks: (::Bool, =false) boolean to show simulation blocks\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nrange: (::Vector{Float64}, =[]) time range to be displayed initially\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the raw signal\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/3.koma_paper/comparison_accuracy/sequences/EPI/epi_100x100_TE100_FOV230.seq\");\n\njulia> sys, obj, seq = Scanner(), brain_phantom2D(), read_seq(seq_file)\n\njulia> raw = simulate(obj, seq, sys)\n\njulia> plot_signal(raw)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_image","page":"API Documentation","title":"plot_image","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_image","category":"page"},{"location":"api/#KomaMRIPlots.plot_image","page":"API Documentation","title":"KomaMRIPlots.plot_image","text":"p = plot_image(image; height, width, zmin, zmax, darkmode, title)\n\nPlots an image matrix.\n\nArguments\n\nimage: (::Matrix{Float64}) image matrix\n\nKeywords\n\nheight: (::Int64, =750) height of the plot\nwidth: (::Int64, =nothing) width of the plot\nzmin: (::Float64, =minimum(abs.(image[:]))) reference value for minimum color\nzmax: (::Float64, =maximum(abs.(image[:]))) reference value for maximum color\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\ntitle: (::String, =\"\") title of the plot\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the image matrix\n\n\n\n\n\n","category":"function"},{"location":"api/#UI","page":"API Documentation","title":"UI","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"CurrentModule = KomaMRI","category":"page"},{"location":"api/#KomaUI","page":"API Documentation","title":"KomaUI","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"KomaUI","category":"page"},{"location":"api/#KomaMRI.KomaUI","page":"API Documentation","title":"KomaMRI.KomaUI","text":"out = KomaUI(; kwargs...)\n\nLaunch the Koma's UI.\n\nKeywords\n\ndarkmode: (::Bool, =true) define dark mode style for the UI\nframe: (::Bool, =true) display the upper frame of the Blink window\nphantom_mode: (::String, =\"2D\", opts=[\"2D\", \"3D\"]) load the default phantom as a 2D or 3D brain example\nsim: (::Dict{String,Any}, =Dict{String,Any}()) simulation parameters dictionary\nrec: (::Dict{Symbol,Any}, =Dict{Symbol,Any}()) reconstruction parameters dictionary\nreturn_window: (::Bool, =false) make the out be either 'nothing' or the Blink window, depending on whether the return_window keyword argument is set to true\nshow_window: (::Bool, =true) display the Blink window\n\nReturns\n\nout: (::Nothing or ::Blink.AtomShell.Window) returns either 'nothing' or the Blink window, depending on whether the return_window keyword argument is set to true.\n\nExamples\n\njulia> KomaUI()\n\n\n\n\n\n","category":"function"},{"location":"events/#Event-Definitions","page":"Events Definition","title":"Event Definitions","text":"","category":"section"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"We refer to RF, Grad, and ADC as \"events\". This section covers the details of how events are defined and manipulated within a Sequence struct.","category":"page"},{"location":"events/#Sequence-Events","page":"Events Definition","title":"Sequence Events","text":"","category":"section"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"As we already know, a Sequence struct contains field names that store arrays of RF, Grad and ADC structs. To create a Sequence, it's essential to understand how to create these fundamental events.","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"In the following subsections, we will provide detailed explanations of event parameters and how to create a Sequence using RF, Grad and ADC events.","category":"page"},{"location":"events/#RF","page":"Events Definition","title":"RF","text":"","category":"section"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"The RF struct is defined in the source code of KomaMRI as follows:","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"mutable struct RF\n A\n T\n Δf\n delay::Real\nend","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"As you can see, it has 4 field names: ''A'' defines amplitude, ''T'' defines duration time, ''delay'' is the distance between the 0 time and the first waveform sample and ''Δf'' is the displacement respect to the main field carrier frequency (this is for advanced users).","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"''A'' and ''T'' can be numbers or vectors of numbers. Depending on the length of the ''A'' and ''T'', KomaMRI interprets different waveforms: ","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"Pulse Waveform: A and T are numbers\nUniformly-Sampled Waveform: A is a vector and T is a number\nTime-Shaped Waveform: A and T are both vectors with the same length (zero-order-hold)","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"In the image below, we provide a summary of how you can define RF events:","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"warning: Warning\nIn future versions of KomaMRI, the RF interpolation will change to use linear interpolation between two consecutive samples, similar to what is currently done with the Grad struct.","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"Let's look at some basic examples of creating these RF structs and including them in a Sequence struct. The examples should be self-explanatory.","category":"page"},{"location":"events/#RF-Pulse-Waveform","page":"Events Definition","title":"RF Pulse Waveform","text":"","category":"section"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"julia> A, T, delay = 10e-3, 0.5e-3, 0.1e-3;\n\njulia> rf = RF(A, T, 0, delay)\n←0.1 ms→ RF(10000.0 uT, 0.5 ms, 0.0 Hz)\n\njulia> seq = Sequence(); seq += rf; seq = seq[2:end]\nSequence[ τ = 0.6 ms | blocks: 1 | ADC: 0 | GR: 0 | RF: 1 | DEF: 0 ]\n\njulia> plot_seq(seq)","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"","category":"page"},{"location":"events/#RF-Uniformly-Sampled-Waveform","page":"Events Definition","title":"RF Uniformly-Sampled Waveform","text":"","category":"section"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"julia> tl = -3:0.2:-0.2; tr = 0.2:0.2:3;\n\njulia> A = (10e-3)*[sin.(π*tl)./(π*tl); 1; sin.(π*tr)./(π*tr)];\n\njulia> T, delay = 0.5e-3, 0.1e-3;\n\njulia> rf = RF(A, T, 0, delay)\n←0.1 ms→ RF(∿ uT, 0.5 ms, 0.0 Hz)\n\njulia> seq = Sequence(); seq += rf; seq = seq[2:end]\nSequence[ τ = 0.6 ms | blocks: 1 | ADC: 0 | GR: 0 | RF: 1 | DEF: 0 ]\n\njulia> plot_seq(seq)","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"","category":"page"},{"location":"events/#RF-Time-Shaped-Waveform","page":"Events Definition","title":"RF Time-Shaped Waveform","text":"","category":"section"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"julia> tl = -4:0.2:-0.2; tr = 0.2:0.2:4\n\njulia> A = (10e-3)*[sin.(π*tl)./(π*tl); 1; sin.(π*tr)./(π*tr)]\n\njulia> T = [0.05e-3*ones(length(tl)); 2e-3; 0.05e-3*ones(length(tl))]\n\njulia> delay = 0.1e-3;\n\njulia> rf = RF(A, T, 0, delay)\n←0.1 ms→ RF(∿ uT, 4.0 ms, 0.0 Hz)\n\njulia> seq = Sequence(); seq += rf; seq = seq[2:end]\nSequence[ τ = 4.1 ms | blocks: 1 | ADC: 0 | GR: 0 | RF: 1 | DEF: 0 ]\n\njulia> plot_seq(seq)","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"","category":"page"},{"location":"events/#Gradient","page":"Events Definition","title":"Gradient","text":"","category":"section"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"The Grad struct is defined as follows in the source code of KomaMRI:","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"mutable struct Grad\n A\n T\n rise::Real\n fall::Real\n delay::Real\nend","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"As you can see, it has 5 field names: ''A'' defines amplitude, ''T'' defines duration time, ''delay'' is the distance between the 0 time and the first waveform sample, ''rise'' and ''fall'' are the time durations of the first and last gradient ramps.","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"Just like the RF, ''A'' and ''T'' in the Grad struct can be numbers or vectors of numbers. Depending on the length of the ''A'' and ''T'', KomaMRI interprets different waveforms: ","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"Trapezoidal Waveform: A and T are numbers\nUniformly-Sampled Waveform: A is a vector and T is a number\nTime-Shaped Waveform: A and T are both vectors, A has one sample more the T (linear interpolation)","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"In the image below, we provide a summary of how you can define Grad events:","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"Let's look at some basic examples of creating these Grad structs and including them in a Sequence struct, focusing on the ''x'' component of the gradients. The examples should be self-explanatory.","category":"page"},{"location":"events/#Gradient-Trapezoidal-Waveform","page":"Events Definition","title":"Gradient Trapezoidal Waveform","text":"","category":"section"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"julia> A, T, delay, rise, fall = 50*10e-6, 5e-3, 2e-3, 1e-3, 1e-3;\n\njulia> gr = Grad(A, T, rise, fall, delay)\n←2.0 ms→ Grad(0.5 mT, 0.5 ms, ↑1.0 ms, ↓1.0 ms)\n\njulia> seq = Sequence([gr])\nSequence[ τ = 9.0 ms | blocks: 1 | ADC: 0 | GR: 1 | RF: 0 | DEF: 0 ]\n\njulia> plot_seq(seq)","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"","category":"page"},{"location":"events/#Gradient-Uniformly-Sampled-Waveform","page":"Events Definition","title":"Gradient Uniformly-Sampled Waveform","text":"","category":"section"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"julia> t = 0:0.25:7.5\n\njulia> A = 10*10e-6 * sqrt.(π*t) .* sin.(π*t)\n\njulia> T = 10e-3;\n\njulia> delay, rise, fall = 1e-3, 0, 1e-3;\n\njulia> gr = Grad(A, T, rise, fall, delay)\n←1.0 ms→ Grad(∿ mT, 10.0 ms, ↑0.0 ms, ↓1.0 ms)\n\njulia> seq = Sequence([gr])\nSequence[ τ = 12.0 ms | blocks: 1 | ADC: 0 | GR: 1 | RF: 0 | DEF: 0 ]\n\njulia> plot_seq(seq)","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"","category":"page"},{"location":"events/#Gradient-Time-Shaped-Waveform","page":"Events Definition","title":"Gradient Time-Shaped Waveform","text":"","category":"section"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"julia> A = 50*10e-6*[1; 1; 0.8; 0.8; 1; 1];\n\njulia> T = 1e-3*[5; 0.2; 5; 0.2; 5];\n\njulia> delay, rise, fall = 1e-3, 1e-3, 1e-3;\n\njulia> gr = Grad(A, T, rise, fall, delay)\n←1.0 ms→ Grad(∿ mT, 15.4 ms, ↑1.0 ms, ↓1.0 ms)\n\njulia> seq = Sequence([gr])\nSequence[ τ = 10.75 ms | blocks: 1 | ADC: 0 | GR: 1 | RF: 0 | DEF: 0 ]\n\njulia> plot_seq(seq)","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"","category":"page"},{"location":"events/#ADC","page":"Events Definition","title":"ADC","text":"","category":"section"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"The ADC struct is defined in the KomaMRI source code as follows:","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"mutable struct ADC\n N::Integer\n T::Real\n delay::Real\n Δf::Real\n ϕ::Real\nend","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"As you can see, it has 5 field names: ''N'' defines number of samples, ''T'' defines total acquisition duration, ''delay'' is the distance between the 0 time and the first sampled signal, ''Δf'' and ''ϕ' are factor to correct signal acquisition (for advanced users).","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"In the image below you can see how to define an ADC event:","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"Let's look at a basic example of defining an ADC struct and including it in a Sequence struct:","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"julia> N, T, delay = 16, 5e-3, 1e-3;\n\njulia> adc = ADC(N, T, delay)\nADC(16, 0.005, 0.001, 0.0, 0.0)\n\njulia> seq = Sequence(); seq += adc; seq = seq[2:end]\nSequence[ τ = 6.0 ms | blocks: 1 | ADC: 1 | GR: 0 | RF: 0 | DEF: 0 ]\n\njulia> plot_seq(seq)","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"","category":"page"},{"location":"events/#Combination-of-Events","page":"Events Definition","title":"Combination of Events","text":"","category":"section"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"We can include multiple events within a single block of a sequence. The example below demonstrates how to combine an RF struct, three Grad structs for the x-y-z components, and an ADC struct in a single block of a sequence:","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"# Define an RF struct\nA, T = 1e-6*[0; -0.1; 0.2; -0.5; 1; -0.5; 0.2; -0.1; 0], 0.5e-3;\nrf = RF(A, T)\n\n# Define a Grad struct for Gx\nA, T, rise = 50*10e-6, 5e-3, 1e-3\ngx = Grad(A, T, rise)\n\n# Define a Grad struct for Gy\nA = 50*10e-6*[0; 0.5; 0.9; 1; 0.9; 0.5; 0; -0.5; -0.9; -1]\nT, rise = 5e-3, 2e-3;\ngy = Grad(A, T, rise)\n\n# Define a Grad struct for Gz\nA = 50*10e-6*[0; 0.5; 0.9; 1; 0.9; 0.5; 0; -0.5; -0.9; -1]\nT = 5e-3*[0.0; 0.1; 0.3; 0.2; 0.1; 0.2; 0.3; 0.2; 0.1]\ngz = Grad(A, T)\n\n# Define an ADC struct\nN, T, delay = 16, 5e-3, 1e-3\nadc = ADC(N, T, delay)","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"julia> seq = Sequence([gx; gy; gz;;], [rf;;], [adc])\nSequence[ τ = 9.0 ms | blocks: 1 | ADC: 1 | GR: 3 | RF: 1 | DEF: 0 ]\n\njulia> plot_seq(seq)","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"","category":"page"},{"location":"events/","page":"Events Definition","title":"Events Definition","text":"Once the struct events are defined, it's important to note that to create a single block sequence, you need to provide 2D matrices of Grad and RF structs, as well as a vector of ADC structs as arguments in the Sequence constructor.","category":"page"},{"location":"ui-details/#User-Interface","page":"Simulation with User Interface","title":"User Interface","text":"","category":"section"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"This section explains how to use the user interface of the KomaMRI package and the internal processes during interaction.","category":"page"},{"location":"ui-details/#Basic-Workflow","page":"Simulation with User Interface","title":"Basic Workflow","text":"","category":"section"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"As a general overview, remember the following workflow steps when using KomaMRI:","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"Loading Simulation Inputs: Scanner, Phantom, Sequence\nRunning Simulation\nReconstructing Image using MRIReco","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"In the following subsections, we will cover all the mentioned steps. First, open the Julia REPL and enter the following commands to include the KomaMRI package and launch the user interface:","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"julia> using KomaMRI\n\njulia> KomaUI()","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"","category":"page"},{"location":"ui-details/#Loading-Simulation-Inputs","page":"Simulation with User Interface","title":"Loading Simulation Inputs","text":"","category":"section"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"The user interface has preloaded certain inputs into RAM, including the Scanner, Phantom, and Sequence structs. In the following subsections, we will demonstrate how to visualize these inputs.","category":"page"},{"location":"ui-details/#Scanner","page":"Simulation with User Interface","title":"Scanner","text":"","category":"section"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"You can visualize the preloaded Scanner struct by clicking on the Scanner dropdown and then pressing the View Scanner button. The Scanner struct contains hardware-related information, such as the main magnetic field's magnitude:","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"","category":"page"},{"location":"ui-details/#Phantom","page":"Simulation with User Interface","title":"Phantom","text":"","category":"section"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"To see the phantom already stored in RAM, simply click on the Phantom dropdown an then press the View Phantom button. The preloaded phantom is a slice of a brain:","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"It is also possible to load .h5 phantom files. The KomaMRI.jl has some examples stored at ~/.julia/packages/KomaMRI//examples/2.phantoms/. For instance, let's load the sphere_chemical_shift.h5 file:","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"Note that you can select different spin parameters to visualize like ρ, T1, T2, among others. ","category":"page"},{"location":"ui-details/#Sequence","page":"Simulation with User Interface","title":"Sequence","text":"","category":"section"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"There are two options to visualize the sequence already preloaded in RAM: in the time domain or in the k-space. The preloaded sequence is a single-shot EPI.","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"For visualization of the sequence in the time domain, click on the Sequence dropdown and then press the Sequence (MPS) button:","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"For visualization of the sequence in the k-space, click on the Sequence dropdown and then press the k-space button:","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"You can also display the Moments related to the Sequence by pressing the View Moments and then pressing the buttons for zero, first and second moments.","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"It is also possible to load Pulseq compatible .seq sequence files. The KomaMRI has some examples stored at ~/.julia/packages/KomaMRI//examples/1.sequences/. For instance, let's load the spiral.seq file and view it the time domain and k-space:","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"","category":"page"},{"location":"ui-details/#Running-Simulation","page":"Simulation with User Interface","title":"Running Simulation","text":"","category":"section"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"Once the inputs are loaded in RAM, it is possible to perform the simulation to get the Raw Signal.","category":"page"},{"location":"ui-details/#Simulation-Parameters","page":"Simulation with User Interface","title":"Simulation Parameters","text":"","category":"section"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"To visualize the default simulation parameters, click on the Simulate! dropdown and then press the View Options button:","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"","category":"page"},{"location":"ui-details/#Visualization-of-the-Raw-Signal","page":"Simulation with User Interface","title":"Visualization of the Raw Signal","text":"","category":"section"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"Press the Simulate! button to perform the simulation (this may take a while). Automatically the generated Raw Signal should be displayed or you can click on the Raw Data dropdown and then press the View Raw Data button:","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"","category":"page"},{"location":"ui-details/#Reconstructing-Image-using-MRIReco","page":"Simulation with User Interface","title":"Reconstructing Image using MRIReco","text":"","category":"section"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"Once the Raw Signal is loaded in RAM, it is possible to reconstruct the image.","category":"page"},{"location":"ui-details/#Reconstruction-Parameters","page":"Simulation with User Interface","title":"Reconstruction Parameters","text":"","category":"section"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"To visualize the default reconstruction parameters, click on the Reconstruct! dropdown and then press the View Options button:","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"","category":"page"},{"location":"ui-details/#Visualization-of-the-Image","page":"Simulation with User Interface","title":"Visualization of the Image","text":"","category":"section"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"Press the Reconstruct! button to perform the reconstruction (this may take a while). Automatically the generated Image should be displayed or you can click on the he Reconstruct! dropdown and then press the |Image| button:","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"","category":"page"},{"location":"ui-details/#Exporting-Results-to-.mat-File","page":"Simulation with User Interface","title":"Exporting Results to .mat File","text":"","category":"section"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"The user interface has the option to save the results in .mat format. Simply click on the Export to .mat and you have the alternatives to get data independently or you can press the All button to have all the results given by the simulator:","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"","category":"page"},{"location":"ui-details/","page":"Simulation with User Interface","title":"Simulation with User Interface","text":"So far, and due to limitations of the user interface dependencies, the .mat files are saved in the temporal directory of your computer OS, which can be found by typing the tempdir() command in the Julia REPL.","category":"page"},{"location":"programming-workflow/#Julia-Scripts","page":"Simulation with Scripts","title":"Julia Scripts","text":"","category":"section"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"You should already be familiar with the Graphical User Interface of KomaMRI. However, you can also use this package directly from the Julia REPL or write your own Julia scripts. This allows you to unlock the full potential of KomaMRI, enabling you to utilize more of its functionalities and even test your own MRI ideas.","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"This section demonstrates a basic workflow with KomaMRI through writing your own scripts or entering commands directly into the Julia REPL. Let's begin.","category":"page"},{"location":"programming-workflow/#Basic-Workflow","page":"Simulation with Scripts","title":"Basic Workflow","text":"","category":"section"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"As a general overview, remember the following workflow steps when using KomaMRI:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"Loading Simulation Inputs: Scanner, Phantom, Sequence\nRunning Simulation\nReconstructing Image using MRIReco","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"Let's replicate these previous steps in a Julia script. You will obtain the following code, which you can copy and paste into the Julia REPL:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"# Import the package\nusing KomaMRI\n\n# Define scanner, object and sequence\nsys = Scanner()\nobj = brain_phantom2D()\nseq = PulseDesigner.EPI_example()\n\n# Define simulation parameters and perform simulation\nsim_params = KomaMRICore.default_sim_params() \nraw = simulate(obj, seq, sys; sim_params)\n\n# Auxiliary function for reconstruction\nfunction reconstruct_2d_image(raw::RawAcquisitionData)\n acqData = AcquisitionData(raw)\n acqData.traj[1].circular = false #Removing circular window\n C = maximum(2*abs.(acqData.traj[1].nodes[:])) #Normalize k-space to -.5 to .5 for NUFFT\n acqData.traj[1].nodes = acqData.traj[1].nodes[1:2,:] ./ C\n Nx, Ny = raw.params[\"reconSize\"][1:2]\n recParams = Dict{Symbol,Any}()\n recParams[:reconSize] = (Nx, Ny)\n recParams[:densityWeighting] = true\n rec = reconstruction(acqData, recParams)\n image3d = reshape(rec.data, Nx, Ny, :)\n image2d = (abs.(image3d) * prod(size(image3d)[1:2]))[:,:,1]\n return image2d\nend\n\n# Perform reconstruction to get the image\nimage = reconstruct_2d_image(raw)","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"Let's go through this script step by step.","category":"page"},{"location":"programming-workflow/#Loading-Simulation-Inputs","page":"Simulation with Scripts","title":"Loading Simulation Inputs","text":"","category":"section"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"The inputs of the simulation are created in the following part of the script: ","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"# Define scanner, object and sequence\nsys = Scanner()\nobj = brain_phantom2D()\nseq = PulseDesigner.EPI_example()","category":"page"},{"location":"programming-workflow/#Scanner","page":"Simulation with Scripts","title":"Scanner","text":"","category":"section"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"The previously created Scanner struct contains default parameters. In your initial simulations, you will likely use this default struct without making any modifications. You can view all the parameters by displaying the struct variable in the Julia REPL. The Scanner's parameters include hardware limitations such as the main magnetic field, maximum gradient values, minimum raster times, and more. You may want to adjust these values for your future custom simulations.","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"julia> sys\nScanner\n B0: Float64 1.5\n B1: Float64 1.0e-5\n Gmax: Float64 0.06\n Smax: Int64 500\n ADC_Δt: Float64 2.0e-6\n seq_Δt: Float64 1.0e-5\n GR_Δt: Float64 1.0e-5\n RF_Δt: Float64 1.0e-6\n RF_ring_down_T: Float64 2.0e-5\n RF_dead_time_T: Float64 0.0001\n ADC_dead_time_T: Float64 1.0e-5","category":"page"},{"location":"programming-workflow/#Phantom","page":"Simulation with Scripts","title":"Phantom","text":"","category":"section"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"The Phantom struct created in this example represents a slice of a brain. To create it, we use the function brain_phantom2D, which is part of the subdependency KomaMRICore. While KomaMRI provides some phantom examples for experimentation, you may also want to create your custom Phantom struct tailored to your specific requirements.","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"The Phantom struct contains MRI parameters related to the magnetization properties of an object. These parameters include magnetization positions, proton density, relaxation times, off-resonance, among others. To view all the keys and values of the object, you can do so in the Julia REPL as follows:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"julia> obj\nPhantom{Float64}\n name: String \"brain2D_axial\"\n x: Array{Float64}((6506,)) [-0.084, -0.084, … 0.086, 0.086]\n y: Array{Float64}((6506,)) [-0.03, -0.028, … 0.0, 0.002]\n z: Array{Float64}((6506,)) [-0.0, -0.0, … 0.0, 0.0]\n ρ: Array{Float64}((6506,)) [0.7, 0.7, … 0.7, 0.7]\n T1: Array{Float64}((6506,)) [0.569, 0.569, … 0.569, 0.569]\n T2: Array{Float64}((6506,)) [0.329, 0.329, … 0.329, 0.329]\n T2s: Array{Float64}((6506,)) [0.058, 0.058, … 0.058, 0.058]\n Δw: Array{Float64}((6506,)) [-0.0, -0.0, … -0.0, -0.0]\n Dλ1: Array{Float64}((6506,)) [0.0, 0.0, … 0.0, 0.0]\n Dλ2: Array{Float64}((6506,)) [0.0, 0.0, … 0.0, 0.0]\n Dθ: Array{Float64}((6506,)) [0.0, 0.0, … 0.0, 0.0]\n...","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"As you can see, attributes of the Phantom struct are vectors representing object properties, with each element holding a value associated with a single magnetization.","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"You can also visualize the Phantom struct using the plot_phantom_map function, which is part of the KomaMRIPlots subdependency. This function plots the magnitude of a property for each magnetization at a specific spatial position. You can observe properties such as proton density and relaxation times, so feel free to replace the :ρ symbol with another property of the phantom in the example below:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"julia> plot_phantom_map(obj, :ρ)","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"To utilize test phantoms included with KomaMRI, navigate to the \"examples\" folder and use the read_phantom_jemris function to read a phantom in .h5 format. The following steps outline how to do this in Julia:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"julia> path_koma = dirname(dirname(pathof(KomaMRI)))\njulia> path_sphere = joinpath(path_koma, \"examples\", \"2.phantoms\", \"sphere_chemical_shift.h5\")\njulia> sphere = read_phantom_jemris(path_sphere)\njulia> plot_phantom_map(sphere, :T2)","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"","category":"page"},{"location":"programming-workflow/#Sequence","page":"Simulation with Scripts","title":"Sequence","text":"","category":"section"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"The Sequence struct in the example represents one of the most basic MRI sequences. It excites the object with a 90° RF pulse and then uses EPI gradients to fill the k-space in a \"square\" manner. While you may want to create your sequences for experiments, you can always use some of the examples already available in KomaMRI.","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"In MRI, the sequence must be carefully designed with precise timing to obtain an image. It includes subcomponents such as gradients, radio-frequency excitation signals, and sample acquisition. For more information on constructing a Sequence struct, refer to the Sequence section.","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"You can view general information about a Sequence struct by displaying it in the Julia REPL:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"julia> seq\nSequence[ τ = 62.846 ms | blocks: 204 | ADC: 101 | GR: 205 | RF: 1 | DEF: 5 ]","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"For more precise timing checks, you can use the plot_seq function:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"julia> plot_seq(seq; range=[0 30])","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"It is important to consider how the sequence traverses through k-space. The get_kspace function does precisely that:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"julia> plot_kspace(seq)","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"Additionally, there are helpful sequence construction functions within a submodule of KomaMRI called PulseDesigner. These functions include RF_hard, RF_sinc, EPI, radial_base and spiral_base. For more details on how to use them, refer to the API documentation.","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"KomaMRI is also compatible with Pulseq. The package installation includes some .seq files in Pulseq format, which can be read and used as a Sequence struct. Here's how to read a spiral Pulseq file stored in the \"examples\" folder of KomaMRI:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"julia> path_koma = dirname(dirname(pathof(KomaMRI)))\njulia> path_spiral = joinpath(path_koma, \"examples\", \"1.sequences\", \"spiral.seq\")\njulia> spiral = read_seq(path_spiral)\njulia> plot_seq(spiral)\njulia> plot_kspace(spiral)","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"","category":"page"},{"location":"programming-workflow/#Running-Simulation","page":"Simulation with Scripts","title":"Running Simulation","text":"","category":"section"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"The following lines in the example script configure and perform the simulation:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"# Define simulation parameters and perform simulation\nsim_params = KomaMRICore.default_sim_params() \nraw = simulate(obj, seq, sys; sim_params)","category":"page"},{"location":"programming-workflow/#Simulation-Parameters","page":"Simulation with Scripts","title":"Simulation Parameters","text":"","category":"section"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"To perform simulations, KomaMRI requires certain parameters. You can use the default parameters for testing, but you also have the option to customize specific simulation aspects. In the example, we use the default_sim_params function to create a dictionary with default simulation parameters. You can view the keys that can be modified by displaying the sim_params variable:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"julia> sim_params\nDict{String, Any} with 9 entries:\n \"return_type\" => \"raw\"\n \"Nblocks\" => 20\n \"gpu\" => true\n \"Nthreads\" => 1\n \"gpu_device\" => 0\n \"sim_method\" => Bloch()\n \"precision\" => \"f32\"\n \"Δt\" => 0.001\n \"Δt_rf\" => 5.0e-5","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"All of these parameters deserve special attention. We will explain some of the most important ones here. For instance, \"Δt\" and \"Δt_rf\" represent the raster times for the gradients and RFs. \"return_type\" specifies the type of variable returned by the simulator (by default, it returns an object ready for use with MRIReco for reconstruction, but you can use the value \"mat\" to return a simple vector). \"gpu\" indicates whether you want to use your GPU device for simulations, and \"precision\" sets the floating-point precision. For more details on how to set these parameters, please refer to the Simulation Parameters Section.","category":"page"},{"location":"programming-workflow/#Raw-Signal","page":"Simulation with Scripts","title":"Raw Signal","text":"","category":"section"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"The simulation is performed using the simulate function, which requires three arguments: a Scanner struct, a Phantom struct, and a Sequence struct. Optionally, you can include the keyword argument sim_params if you wish to use custom simulation parameters.","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"In the example, we can see that the output of the simulation is a special struct:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"julia> typeof(raw)\nRawAcquisitionData\n\njulia> raw\nRawAcquisitionData[SeqName: epi | 101 Profile(s) of 101×1]","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"You can plot the simulation result with the plot_signal function like so:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"julia> plot_signal(raw)","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"","category":"page"},{"location":"programming-workflow/#Reconstructing-Image-using-MRIReco","page":"Simulation with Scripts","title":"Reconstructing Image using MRIReco","text":"","category":"section"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"KomaMRI does not handle reconstruction; instead, you should utilize the MRIReco package to generate an image. For convenience, when you install KomaMRI, you also install MRIReco, allowing you to access functions from that package. You should pay special attention to the RawAcquisitionData and AcquisitionData structs, as well as the reconstruction function.","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"In the example below, we define an auxiliary function, reconstruct_2d_image, which takes a raw signal struct, RawAcquisitionData, as input and returns a 2D Array representing an image. Within this function, we create an AcquisitionData struct and set some reconstruction parameters, which serve as inputs for the reconstruction function. The latter function is responsible for the image generation process.","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"# Auxiliary function for reconstruction\nfunction reconstruct_2d_image(raw::RawAcquisitionData)\n acqData = AcquisitionData(raw)\n acqData.traj[1].circular = false #Removing circular window\n C = maximum(2*abs.(acqData.traj[1].nodes[:])) #Normalize k-space to -.5 to .5 for NUFFT\n acqData.traj[1].nodes = acqData.traj[1].nodes[1:2,:] ./ C\n Nx, Ny = raw.params[\"reconSize\"][1:2]\n recParams = Dict{Symbol,Any}()\n recParams[:reconSize] = (Nx, Ny)\n recParams[:densityWeighting] = true\n rec = reconstruction(acqData, recParams)\n image3d = reshape(rec.data, Nx, Ny, :)\n image2d = (abs.(image3d) * prod(size(image3d)[1:2]))[:,:,1]\n return image2d\nend\n\n# Perform reconstruction to get the image\nimage = reconstruct_2d_image(raw)","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"If you need more information about how to use the AcquisitionData and the how to fill the reconstruction parameters, you need to visit the MRIReco webpage).","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"To display the image, you can use the plot_image function which is part of the KomaMRIPlots subpackage:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"julia> plot_image(image)","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"
","category":"page"},{"location":"programming-workflow/#Exporting-Results-to-.mat-File","page":"Simulation with Scripts","title":"Exporting Results to .mat File","text":"","category":"section"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"Many people in the MRI community uses MATLAB, probably you are one of them and you want to process the raw signal in the MATLAB environment after simulation is done with KomaMRI. Here we show you an example of how to save a .mat file with the information of the raw signal thank to the help of the MAT package:","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"Many people in the MRI community use MATLAB; you might be one of them and may want to process the Raw Signal in the MATLAB environment after simulation is completed with KomaMRI. Here, we provide an example of how to save a .mat file containing the Raw Signal information using the MAT package.","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"# Use the MAT package\nusing MAT\n\n# Perform simulation to return an Array type\nsim_params[\"return_type\"] = \"mat\"\nraw = simulate(obj, seq, sys; sim_params)\n\n# Save the .mat file in the temp directory\nmatwrite(joinpath(tempdir(), \"koma-raw.mat\"), Dict(\"raw\" => raw))","category":"page"},{"location":"programming-workflow/","page":"Simulation with Scripts","title":"Simulation with Scripts","text":"Note that we need to simulate to return an array type (not the default RawAcquisitionData), and then we utilize the matwrite function to save a file named \"koma-raw.mat\" in your computer's temporary directory. Now, you can navigate to your temporary directory (which you can find by displaying the result of tempdir() in the Julia REPL) and locate the \"koma-raw.mat\" file.","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"EditURL = \"../../../../examples/literate/examples/03-ChemicalShiftEPI.jl\"","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/#Chemical-Shift-in-an-EPI-sequence","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"","category":"section"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"(Image: ) (Image: )","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"using KomaMRI # hide\nsys = Scanner() # hide","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"For a more realistic example, we will use a brain phantom.","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"obj = brain_phantom2D() # a slice of a brain\np1 = plot_phantom_map(obj, :T2 ; height=400, width=400, view_2d=true)\np2 = plot_phantom_map(obj, :Δw ; height=400, width=400, view_2d=true)\nsavefig(p1, \"../../assets/examples/2-phantom1.html\") # hide\nsavefig(p2, \"../../assets/examples/2-phantom2.html\") # hide","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"At the left, you can see the T_2 map of the phantom, and at the right, the off-resonance Deltaomega. In this example, the fat is the only source of off-resonance (with Delta f = -220mathrmHz) and you can see it in black in the off-resonance map.","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"Then, we will load an EPI sequence, that is well known for being affected by off-resonance. With this sequence, we will be able visualize the effect of the chemical shift.","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/3.koma_paper/comparison_accuracy/sequences/EPI/epi_100x100_TE100_FOV230.seq\")\nseq = read_seq(seq_file)\np3 = plot_seq(seq; range=[0 40], slider=true, height=300)\nsavefig(p3, \"../../assets/examples/2-seq.html\") # hide","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"Feel free to explore the sequence's plot 🔍 below!","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"If we simulate this sequence we will end up with the following signal.","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"raw = simulate(obj, seq, sys)\np4 = plot_signal(raw; range=[98.4 103.4] , height=300)\nsavefig(p4, \"../../assets/examples/2-signal.html\") # hide","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"Now, we need to inspect what effect the off-resonance had in the reconstructed image. As you can see, the fat layer is now shifted to a different position 🤯, this is why the effect is called chemical shift!","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"# Get the acquisition data\nacq = AcquisitionData(raw)\nacq.traj[1].circular = false #This is to remove the circular mask\n\n# Setting up the reconstruction parameters\nNx, Ny = raw.params[\"reconSize\"][1:2]\nreconParams = Dict{Symbol,Any}(:reco=>\"direct\", :reconSize=>(Nx, Ny))\nimage = reconstruction(acq, reconParams)\n\n# Plotting the recon\nslice_abs = abs.(image[:, :, 1])\np5 = plot_image(slice_abs; height=400)\nsavefig(p5, \"../../assets/examples/2-recon.html\") # hide","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"
","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"This page was generated using Literate.jl.","category":"page"},{"location":"mri-theory/#Simulation","page":"Simulation","title":"Simulation","text":"","category":"section"},{"location":"mri-theory/#General-Overview","page":"Simulation","title":"General Overview","text":"","category":"section"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"KomaMRI simulates the magnetization of each spin of a Phantom for variable magnetic fields given by a Sequence. It is assumed that a single spin is independent of the state of the other spins in the system (a key feature that enables parallelization). Furthermore, there are defined two regimes in the Sequence: excitation and precession. During the latter, the excitation fields are nulled and are useful for simplifying some physical equations.","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"The are more internal considerations in the KomaMRI implementation. The Figure 1 summarizes the functions called to perform the simulation.","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"
\n\n
","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"From the programming perspective, it is needed to call the function simulate with the sim_params dictionary keyword argument. A user can change the values of the following keys:","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"\"return_type\": defines the output of the simulate function. Possible values are \"raw\", \"mat\", and \"state\", corresponding to outputting a MRIReco RawAcquisitionData, the signal values, and the last magnetization state of the simulation, respectively.\n\"sim_method\": defines the type of simulation. The default value is Bloch(), but you can alternatively use the BlochDict() simulation method. Moreover, you have the flexibility to create your own methods without altering the KomaMRI source code; for further details, refer to the Simulation Method Extensibility section.\n\"Δt\": raster time for gradients.\n\"Δt_rf\": raster time for RFs.\n\"precision\": defines the floating-point simulation precision. You can choose between \"f32\" and \"f64\" to use Float32 and Float64 primitive types, respectively. It's important to note that, especially for GPU operations, using \"f32\" is generally much faster.\n\"Nblocks\" divides the simulation into a specified number of time blocks. This parameter is designed to conserve RAM resources, as KomaMRI computes a series of simulations consecutively, each with the specified number of blocks determined by the value of \"Nblocks\".\n\"Nthreads\": divides the Phantom into a specified number of threads. Because spins are modeled independently of each other, KomaMRI can solve simulations in parallel threads, speeding up the execution time.\n\"gpu\": is a boolean that determines whether to use GPU or CPU hardware resources, as long as they are available on the host computer.\n\"gpu_device\": sets the index ID of the available GPU in the host computer.","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"For instance, if you want to perform a simulation on the CPU with float64 precision using the BlochDict() method (assuming you have already defined obj and seq), you can do so like this:","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"# Set non-default simulation parameters and run simulation\nsim_params = KomaMRICore.default_sim_params() \nsim_params[\"gpu\"] = false\nsim_params[\"precision\"] = \"f64\"\nsim_params[\"sim_method\"] = BlochDict()\nraw = simulate(obj, seq, sys; sim_params)","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"Additionally, the user must be aware of the functions run_spin_excitation! and run_spin_precession! which defines the algorithm for excitation and precession regimes respectively and can be changed by the user without modifying the source code (more details at Simulation Method Extensibility).","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"Previous simulation, the Sequence is discretized to consider specific time points which are critical for simulation. The user can control the time between intermediate gradient samples with the parameter Δt. Similarly, the parameter Δt_rf manages the time between RF samples, and can be relatively large for 2D imaging where the slice profile is less relevant.","category":"page"},{"location":"mri-theory/#Computation-Efficiency","page":"Simulation","title":"Computation Efficiency","text":"","category":"section"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"To reduce the memory usage of our simulator, we subdivided time into Nblocks. KomaMRI classifies each block in either the excitation regime or the precession regime before the simulation.","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"We increased the simulation speed by separating the calculations into Nthreads and then performing the GPU parallel operations with CUDA.jl . This separation is possible as all magnetization vectors are independent of one another.","category":"page"},{"location":"mri-theory/#Simulation-Method-Extensibility","page":"Simulation","title":"Simulation Method Extensibility","text":"","category":"section"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"In Julia, functions use different methods based on the input types via multiple dispatch. We used this to specialize the simulation functions for a given sim_method <:SimulationMethod specified in sim_params. For a given simulation method, the function initialize_spin_state outputs a variable Xt <: SpinStateRepresentation that is passed through the simulation (Figure 1). For the default simulation method Bloch, the spin state is of type Mag, but can be extended to a custom representation, like for example EPGs44 or others. Then, the functions run_spin_excitation! and run_spin_precession! can be described externally for custom types sim_method and Xt, extending Koma’s functionalities without the need of modifying the source code and taking advantage of all of Koma’s features.","category":"page"},{"location":"mri-theory/#Bloch-Simulation-Method","page":"Simulation","title":"Bloch Simulation Method","text":"","category":"section"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"This is the default simulation method used by KomaMRI, however it can always be specified by setting the sim_method = Bloch() entry of the sim_params dictionary. In the following subsection, we will explain the physical and mathematical background and some considerations and assumptions that enables to speed up the simulation.","category":"page"},{"location":"mri-theory/#Physical-and-Mathematical-Background","page":"Simulation","title":"Physical and Mathematical Background","text":"","category":"section"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"The Bloch method of KomaMRI simulates the magnetization of each spin by solving the Bloch equations in the rotating frame:","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"beginalign tag1\n\nfracmathrmd boldsymbolMmathrmd t =\n gamma boldsymbolM times boldsymbolB\n- fracM_x hatx + M_y hatyT_2\n- fracM_z hatx + M_0 hatyT_1 \n\nendalign","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"with gamma the gyromagnetic ratio, boldsymbolM = M_x M_y M_z^T the magnetization vector, and","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"boldsymbolB = B_1x(t) B_1y(t) boldsymbolG(t) cdot boldsymbolx + Delta omega(t)^T","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"the effective magnetic field. M_0 is the proton density, T_1 and T_2 are the relaxation times, and Delta omega is the off-resonance, for each position.","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"The Bloch Simulation Method also uses the technique of operator splitting to simplify the solution of Equation (1). This reflects mathematically the intuition of separating the Bloch equations in a rotation operator described by","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"beginalign tag2\n\nfracmathrmdmathrmdt boldsymbolM =\nbeginbmatrix\n 0 gamma B_z -gamma B_y \n-gamma B_z 0 gamma B_x \n gamma B_y -gamma B_x 0\nendbmatrix\nboldsymbolM \n\nendalign","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"and a relaxation operator described by","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"beginalign tag3\n\nfracmathrmdmathrmdt boldsymbolM =\nbeginbmatrix\n-tfrac1T_2 0 0 \n0 -tfrac1T_2 0 \n0 0 -tfrac1T_1\nendbmatrix\nboldsymbolM\n+\nbeginbmatrix\n0 \n0 \ntfracM_0T_1\nendbmatrix \n\nendalign","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"The evolution of the magnetization can then be described as a two-step process for each time step Delta t (Figure 2).","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"
\n\n
","category":"page"},{"location":"mri-theory/#BlochDict-Simulation-Method","page":"Simulation","title":"BlochDict Simulation Method","text":"","category":"section"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"This is another simulation method defined in the source code of KomaMRI. It can be specified by setting the sim_method = BlochDict() entry of the sim_params dictionary.","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"note: Note\nThis section is under construction. More explanation of this simulation method is required.","category":"page"},{"location":"#Introduction","page":"Home","title":"Introduction","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"KomaMRI is a Julia package meant to simulate general Magnetic Resonance Imaging (MRI) scenarios. Its name comes from the Japanese word for spinning-top こま (ko-ma) as they precess due to gravity like spins in a magnetic field.","category":"page"},{"location":"","page":"Home","title":"Home","text":"KomaMRI generates raw data by solving the Bloch equations using the specified scanner, phantom and sequence. It also provides a Graphical User Interface (GUI) that encapsulates the whole imaging pipeline (simulation and reconstruction).","category":"page"},{"location":"","page":"Home","title":"Home","text":"\n","category":"page"},{"location":"","page":"Home","title":"Home","text":"KomaMRI can be used by either:","category":"page"},{"location":"","page":"Home","title":"Home","text":"User Interface: User-friendly interaction. No Julia programming skills are required. Refer to Simulation with User Interface to dive into the details of how to use the GUI.\nScripts : Basic knowledge of Julia is required. Refer to Simulation with Scripts to follow a basic workflow on how to work with KomaMRI.","category":"page"},{"location":"","page":"Home","title":"Home","text":"If you are new to KomaMRI, we recommend starting with the \"Getting Started\" section to install Julia, KomaMRI, and perform your first simulation.","category":"page"},{"location":"#Features","page":"Home","title":"Features","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Some of the features of KomaMRI are:","category":"page"},{"location":"","page":"Home","title":"Home","text":"Fast simulations by using CPU and GPU parallelization 🏃💨.\nOpen Source, so anyone can include additional features 🆙.\nCompatibility with community-standards 🤝 like Pulseq .seq and ISMRMRD .mrd.\nCompatibility with Pluto and Jupyter notebooks 🎈\nCross-platform 🌐 thanks to the use of the Julia programming language.\nFriendly user interface for people with no programming skills 😌.\nFlexible API for advanced users 👨💻.","category":"page"},{"location":"#Potential-Use-Cases","page":"Home","title":"Potential Use Cases","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"We see Koma being used in:","category":"page"},{"location":"","page":"Home","title":"Home","text":"The generation of synthetic data to train Machine Learning models.\nTo test novel pulse sequences before implementing them directly in a real scanner (with a Pulseq sequence).\nTeaching exercises for MRI acquisition or reconstruction.","category":"page"}]
+[{"location":"ways-of-using-koma/#Ways-of-using-Koma","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"KomaMRI can be used in different environments, depending on the degree of flexibility you desire. If you lack prior programming knowledge, we recommend starting with the User Interface. If you seek the full range of flexibility that KomaMRI offers, programming with Scripts is likely your preference. Alternatively, you can utilize the programming environment provided by Notebooks. Detailed explanations for each method of using KomaMRI will be provided in the following sections.","category":"page"},{"location":"ways-of-using-koma/#User-Interface","page":"Ways of using Koma","title":"User Interface","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"This section explains how to use the user interface of the KomaMRI package and the internal processes during interaction.","category":"page"},{"location":"ways-of-using-koma/#Basic-Workflow","page":"Ways of using Koma","title":"Basic Workflow","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"As a general overview, remember the following workflow steps when using KomaMRI:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Loading Simulation Inputs: Scanner, Phantom, Sequence\nRunning Simulation\nReconstructing Image using MRIReco","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"In the following subsections, we will cover all the mentioned steps. First, open the Julia REPL and enter the following commands to include the KomaMRI package and launch the user interface:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia> using KomaMRI\n\njulia> KomaUI()","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/#Loading-Simulation-Inputs","page":"Ways of using Koma","title":"Loading Simulation Inputs","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"The user interface has preloaded certain inputs into RAM, including the Scanner, Phantom, and Sequence structs. In the following subsections, we will demonstrate how to visualize these inputs.","category":"page"},{"location":"ways-of-using-koma/#Scanner","page":"Ways of using Koma","title":"Scanner","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"You can visualize the preloaded Scanner struct by clicking on the Scanner dropdown and then pressing the View Scanner button. The Scanner struct contains hardware-related information, such as the main magnetic field's magnitude:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/#Phantom","page":"Ways of using Koma","title":"Phantom","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"To see the phantom already stored in RAM, simply click on the Phantom dropdown an then press the View Phantom button. The preloaded phantom is a slice of a brain:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"It is also possible to load .h5 phantom files. The KomaMRI.jl has some examples stored at ~/.julia/packages/KomaMRI//examples/2.phantoms/. For instance, let's load the sphere_chemical_shift.h5 file:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Note that you can select different spin parameters to visualize like ρ, T1, T2, among others. ","category":"page"},{"location":"ways-of-using-koma/#Sequence","page":"Ways of using Koma","title":"Sequence","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"There are two options to visualize the sequence already preloaded in RAM: in the time domain or in the k-space. The preloaded sequence is a single-shot EPI.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"For visualization of the sequence in the time domain, click on the Sequence dropdown and then press the Sequence (MPS) button:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"For visualization of the sequence in the k-space, click on the Sequence dropdown and then press the k-space button:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"You can also display the Moments related to the Sequence by pressing the View Moments and then pressing the buttons for zero, first and second moments.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"It is also possible to load Pulseq compatible .seq sequence files. The KomaMRI has some examples stored at ~/.julia/packages/KomaMRI//examples/1.sequences/. For instance, let's load the spiral.seq file and view it the time domain and k-space:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/#Running-Simulation","page":"Ways of using Koma","title":"Running Simulation","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Once the inputs are loaded in RAM, it is possible to perform the simulation to get the Raw Signal.","category":"page"},{"location":"ways-of-using-koma/#Simulation-Parameters","page":"Ways of using Koma","title":"Simulation Parameters","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"To visualize the default simulation parameters, click on the Simulate! dropdown and then press the View Options button:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/#Visualization-of-the-Raw-Signal","page":"Ways of using Koma","title":"Visualization of the Raw Signal","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Press the Simulate! button to perform the simulation (this may take a while). Automatically the generated Raw Signal should be displayed or you can click on the Raw Data dropdown and then press the View Raw Data button:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/#Reconstructing-Image-using-MRIReco","page":"Ways of using Koma","title":"Reconstructing Image using MRIReco","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Once the Raw Signal is loaded in RAM, it is possible to reconstruct the image.","category":"page"},{"location":"ways-of-using-koma/#Reconstruction-Parameters","page":"Ways of using Koma","title":"Reconstruction Parameters","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"To visualize the default reconstruction parameters, click on the Reconstruct! dropdown and then press the View Options button:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/#Visualization-of-the-Image","page":"Ways of using Koma","title":"Visualization of the Image","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Press the Reconstruct! button to perform the reconstruction (this may take a while). Automatically the generated Image should be displayed or you can click on the he Reconstruct! dropdown and then press the |Image| button:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/#Exporting-Results-to-.mat-File","page":"Ways of using Koma","title":"Exporting Results to .mat File","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"The user interface has the option to save the results in .mat format. Simply click on the Export to .mat and you have the alternatives to get data independently or you can press the All button to have all the results given by the simulator:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"So far, and due to limitations of the user interface dependencies, the .mat files are saved in the temporal directory of your computer OS, which can be found by typing the tempdir() command in the Julia REPL.","category":"page"},{"location":"ways-of-using-koma/#Julia-Scripts","page":"Ways of using Koma","title":"Julia Scripts","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"You should already be familiar with the Graphical User Interface of KomaMRI. However, you can also use this package directly from the Julia REPL or write your own Julia scripts. This allows you to unlock the full potential of KomaMRI, enabling you to utilize more of its functionalities and even test your own MRI ideas.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"This section demonstrates a basic workflow with KomaMRI through writing your own scripts or entering commands directly into the Julia REPL. Let's begin.","category":"page"},{"location":"ways-of-using-koma/#Basic-Workflow-2","page":"Ways of using Koma","title":"Basic Workflow","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"As a general overview, remember the following workflow steps when using KomaMRI:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Loading Simulation Inputs: Scanner, Phantom, Sequence\nRunning Simulation\nReconstructing Image using MRIReco","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Let's replicate these previous steps in a Julia script. You will obtain the following code, which you can copy and paste into the Julia REPL:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"# Import the package\nusing KomaMRI\n\n# Define scanner, object and sequence\nsys = Scanner()\nobj = brain_phantom2D()\nseq = PulseDesigner.EPI_example()\n\n# Define simulation parameters and perform simulation\nsim_params = KomaMRICore.default_sim_params() \nraw = simulate(obj, seq, sys; sim_params)\n\n# Auxiliary function for reconstruction\nfunction reconstruct_2d_image(raw::RawAcquisitionData)\n acqData = AcquisitionData(raw)\n acqData.traj[1].circular = false #Removing circular window\n C = maximum(2*abs.(acqData.traj[1].nodes[:])) #Normalize k-space to -.5 to .5 for NUFFT\n acqData.traj[1].nodes = acqData.traj[1].nodes[1:2,:] ./ C\n Nx, Ny = raw.params[\"reconSize\"][1:2]\n recParams = Dict{Symbol,Any}()\n recParams[:reconSize] = (Nx, Ny)\n recParams[:densityWeighting] = true\n rec = reconstruction(acqData, recParams)\n image3d = reshape(rec.data, Nx, Ny, :)\n image2d = (abs.(image3d) * prod(size(image3d)[1:2]))[:,:,1]\n return image2d\nend\n\n# Perform reconstruction to get the image\nimage = reconstruct_2d_image(raw)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Let's go through this script step by step.","category":"page"},{"location":"ways-of-using-koma/#Loading-Simulation-Inputs-2","page":"Ways of using Koma","title":"Loading Simulation Inputs","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"The inputs of the simulation are created in the following part of the script: ","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"# Define scanner, object and sequence\nsys = Scanner()\nobj = brain_phantom2D()\nseq = PulseDesigner.EPI_example()","category":"page"},{"location":"ways-of-using-koma/#Scanner-2","page":"Ways of using Koma","title":"Scanner","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"The previously created Scanner struct contains default parameters. In your initial simulations, you will likely use this default struct without making any modifications. You can view all the parameters by displaying the struct variable in the Julia REPL. The Scanner's parameters include hardware limitations such as the main magnetic field, maximum gradient values, minimum raster times, and more. You may want to adjust these values for your future custom simulations.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia> sys\nScanner\n B0: Float64 1.5\n B1: Float64 1.0e-5\n Gmax: Float64 0.06\n Smax: Int64 500\n ADC_Δt: Float64 2.0e-6\n seq_Δt: Float64 1.0e-5\n GR_Δt: Float64 1.0e-5\n RF_Δt: Float64 1.0e-6\n RF_ring_down_T: Float64 2.0e-5\n RF_dead_time_T: Float64 0.0001\n ADC_dead_time_T: Float64 1.0e-5","category":"page"},{"location":"ways-of-using-koma/#Phantom-2","page":"Ways of using Koma","title":"Phantom","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"The Phantom struct created in this example represents a slice of a brain. To create it, we use the function brain_phantom2D, which is part of the subdependency KomaMRICore. While KomaMRI provides some phantom examples for experimentation, you may also want to create your custom Phantom struct tailored to your specific requirements.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"The Phantom struct contains MRI parameters related to the magnetization properties of an object. These parameters include magnetization positions, proton density, relaxation times, off-resonance, among others. To view all the keys and values of the object, you can do so in the Julia REPL as follows:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia> obj\nPhantom{Float64}\n name: String \"brain2D_axial\"\n x: Array{Float64}((6506,)) [-0.084, -0.084, … 0.086, 0.086]\n y: Array{Float64}((6506,)) [-0.03, -0.028, … 0.0, 0.002]\n z: Array{Float64}((6506,)) [-0.0, -0.0, … 0.0, 0.0]\n ρ: Array{Float64}((6506,)) [0.7, 0.7, … 0.7, 0.7]\n T1: Array{Float64}((6506,)) [0.569, 0.569, … 0.569, 0.569]\n T2: Array{Float64}((6506,)) [0.329, 0.329, … 0.329, 0.329]\n T2s: Array{Float64}((6506,)) [0.058, 0.058, … 0.058, 0.058]\n Δw: Array{Float64}((6506,)) [-0.0, -0.0, … -0.0, -0.0]\n Dλ1: Array{Float64}((6506,)) [0.0, 0.0, … 0.0, 0.0]\n Dλ2: Array{Float64}((6506,)) [0.0, 0.0, … 0.0, 0.0]\n Dθ: Array{Float64}((6506,)) [0.0, 0.0, … 0.0, 0.0]\n...","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"As you can see, attributes of the Phantom struct are vectors representing object properties, with each element holding a value associated with a single magnetization.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"You can also visualize the Phantom struct using the plot_phantom_map function, which is part of the KomaMRIPlots subdependency. This function plots the magnitude of a property for each magnetization at a specific spatial position. You can observe properties such as proton density and relaxation times, so feel free to replace the :ρ symbol with another property of the phantom in the example below:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia> plot_phantom_map(obj, :ρ)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"To utilize test phantoms included with KomaMRI, navigate to the \"examples\" folder and use the read_phantom_jemris function to read a phantom in .h5 format. The following steps outline how to do this in Julia:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia> path_koma = dirname(dirname(pathof(KomaMRI)))\njulia> path_sphere = joinpath(path_koma, \"examples\", \"2.phantoms\", \"sphere_chemical_shift.h5\")\njulia> sphere = read_phantom_jemris(path_sphere)\njulia> plot_phantom_map(sphere, :T2)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/#Sequence-2","page":"Ways of using Koma","title":"Sequence","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"The Sequence struct in the example represents one of the most basic MRI sequences. It excites the object with a 90° RF pulse and then uses EPI gradients to fill the k-space in a \"square\" manner. While you may want to create your sequences for experiments, you can always use some of the examples already available in KomaMRI.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"In MRI, the sequence must be carefully designed with precise timing to obtain an image. It includes subcomponents such as gradients, radio-frequency excitation signals, and sample acquisition. For more information on constructing a Sequence struct, refer to the Sequence section.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"You can view general information about a Sequence struct by displaying it in the Julia REPL:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia> seq\nSequence[ τ = 62.846 ms | blocks: 204 | ADC: 101 | GR: 205 | RF: 1 | DEF: 5 ]","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"For more precise timing checks, you can use the plot_seq function:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia> plot_seq(seq; range=[0 30])","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"It is important to consider how the sequence traverses through k-space. The get_kspace function does precisely that:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia> plot_kspace(seq)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Additionally, there are helpful sequence construction functions within a submodule of KomaMRI called PulseDesigner. These functions include RF_hard, RF_sinc, EPI, radial_base and spiral_base. For more details on how to use them, refer to the API documentation.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"KomaMRI is also compatible with Pulseq. The package installation includes some .seq files in Pulseq format, which can be read and used as a Sequence struct. Here's how to read a spiral Pulseq file stored in the \"examples\" folder of KomaMRI:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia> path_koma = dirname(dirname(pathof(KomaMRI)))\njulia> path_spiral = joinpath(path_koma, \"examples\", \"1.sequences\", \"spiral.seq\")\njulia> spiral = read_seq(path_spiral)\njulia> plot_seq(spiral)\njulia> plot_kspace(spiral)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/#Running-Simulation-2","page":"Ways of using Koma","title":"Running Simulation","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"The following lines in the example script configure and perform the simulation:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"# Define simulation parameters and perform simulation\nsim_params = KomaMRICore.default_sim_params() \nraw = simulate(obj, seq, sys; sim_params)","category":"page"},{"location":"ways-of-using-koma/#Simulation-Parameters-2","page":"Ways of using Koma","title":"Simulation Parameters","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"To perform simulations, KomaMRI requires certain parameters. You can use the default parameters for testing, but you also have the option to customize specific simulation aspects. In the example, we use the default_sim_params function to create a dictionary with default simulation parameters. You can view the keys that can be modified by displaying the sim_params variable:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia> sim_params\nDict{String, Any} with 9 entries:\n \"return_type\" => \"raw\"\n \"Nblocks\" => 20\n \"gpu\" => true\n \"Nthreads\" => 1\n \"gpu_device\" => 0\n \"sim_method\" => Bloch()\n \"precision\" => \"f32\"\n \"Δt\" => 0.001\n \"Δt_rf\" => 5.0e-5","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"All of these parameters deserve special attention. We will explain some of the most important ones here. For instance, \"Δt\" and \"Δt_rf\" represent the raster times for the gradients and RFs. \"return_type\" specifies the type of variable returned by the simulator (by default, it returns an object ready for use with MRIReco for reconstruction, but you can use the value \"mat\" to return a simple vector). \"gpu\" indicates whether you want to use your GPU device for simulations, and \"precision\" sets the floating-point precision. For more details on how to set these parameters, please refer to the Simulation Parameters Section.","category":"page"},{"location":"ways-of-using-koma/#Raw-Signal","page":"Ways of using Koma","title":"Raw Signal","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"The simulation is performed using the simulate function, which requires three arguments: a Scanner struct, a Phantom struct, and a Sequence struct. Optionally, you can include the keyword argument sim_params if you wish to use custom simulation parameters.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"In the example, we can see that the output of the simulation is a special struct:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia> typeof(raw)\nRawAcquisitionData\n\njulia> raw\nRawAcquisitionData[SeqName: epi | 101 Profile(s) of 101×1]","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"You can plot the simulation result with the plot_signal function like so:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia> plot_signal(raw)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/#Reconstructing-Image-using-MRIReco-2","page":"Ways of using Koma","title":"Reconstructing Image using MRIReco","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"KomaMRI does not handle reconstruction; instead, you should utilize the MRIReco package to generate an image. For convenience, when you install KomaMRI, you also install MRIReco, allowing you to access functions from that package. You should pay special attention to the RawAcquisitionData and AcquisitionData structs, as well as the reconstruction function.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"In the example below, we define an auxiliary function, reconstruct_2d_image, which takes a raw signal struct, RawAcquisitionData, as input and returns a 2D Array representing an image. Within this function, we create an AcquisitionData struct and set some reconstruction parameters, which serve as inputs for the reconstruction function. The latter function is responsible for the image generation process.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"# Auxiliary function for reconstruction\nfunction reconstruct_2d_image(raw::RawAcquisitionData)\n acqData = AcquisitionData(raw)\n acqData.traj[1].circular = false #Removing circular window\n C = maximum(2*abs.(acqData.traj[1].nodes[:])) #Normalize k-space to -.5 to .5 for NUFFT\n acqData.traj[1].nodes = acqData.traj[1].nodes[1:2,:] ./ C\n Nx, Ny = raw.params[\"reconSize\"][1:2]\n recParams = Dict{Symbol,Any}()\n recParams[:reconSize] = (Nx, Ny)\n recParams[:densityWeighting] = true\n rec = reconstruction(acqData, recParams)\n image3d = reshape(rec.data, Nx, Ny, :)\n image2d = (abs.(image3d) * prod(size(image3d)[1:2]))[:,:,1]\n return image2d\nend\n\n# Perform reconstruction to get the image\nimage = reconstruct_2d_image(raw)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"If you need more information about how to use the AcquisitionData and the how to fill the reconstruction parameters, you need to visit the MRIReco webpage).","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"To display the image, you can use the plot_image function which is part of the KomaMRIPlots subpackage:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia> plot_image(image)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"
","category":"page"},{"location":"ways-of-using-koma/#Exporting-Results-to-.mat-File-2","page":"Ways of using Koma","title":"Exporting Results to .mat File","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Many people in the MRI community uses MATLAB, probably you are one of them and you want to process the raw signal in the MATLAB environment after simulation is done with KomaMRI. Here we show you an example of how to save a .mat file with the information of the raw signal thank to the help of the MAT package:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Many people in the MRI community use MATLAB; you might be one of them and may want to process the Raw Signal in the MATLAB environment after simulation is completed with KomaMRI. Here, we provide an example of how to save a .mat file containing the Raw Signal information using the MAT package.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"# Use the MAT package\nusing MAT\n\n# Perform simulation to return an Array type\nsim_params[\"return_type\"] = \"mat\"\nraw = simulate(obj, seq, sys; sim_params)\n\n# Save the .mat file in the temp directory\nmatwrite(joinpath(tempdir(), \"koma-raw.mat\"), Dict(\"raw\" => raw))","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Note that we need to simulate to return an array type (not the default RawAcquisitionData), and then we utilize the matwrite function to save a file named \"koma-raw.mat\" in your computer's temporary directory. Now, you can navigate to your temporary directory (which you can find by displaying the result of tempdir() in the Julia REPL) and locate the \"koma-raw.mat\" file.","category":"page"},{"location":"ways-of-using-koma/#Notebooks","page":"Ways of using Koma","title":"Notebooks","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"You can use KomaMRI with popular programming environments such as Pluto and Jupyter notebooks. The following sections show how to set up these notebooks and test KomaMRI with them.","category":"page"},{"location":"ways-of-using-koma/#Using-KomaMRI-with-Pluto","page":"Ways of using Koma","title":"Using KomaMRI with Pluto","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"First, install the Pluto module in your Julia environment. Remember to press the ] button to open the Package Manager Session:\"","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia>\n\n@(1.9) pkg> add Pluto","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Afterward, return to the Julia Session by pressing the backspace button, and then execute the Pluto.run() function:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia> using Pluto\n\njulia> Pluto.run()","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"This should automatically open the Pluto dashboard in your default web browser:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"
","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Next, create a new notebook by clicking on + Create a new notebook:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"
","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Write and run the following code, which is identical to the Free Induction Decay example. Pluto automatically installs the required modules if they are not present on your system. Additionally, note that we do not use KomaMRI directly since we won't be utilizing the KomaUI() function. Instead, we rely on the KomaMRICore and KomaMRIPlots dependencies. To display plots in Pluto, ensure that you import the PlutoPlots package:\"","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/#Using-KomaMRI-with-Jupyter","page":"Ways of using Koma","title":"Using KomaMRI with Jupyter","text":"","category":"section"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Ensure you have Jupyter installed on your computer. Follow this tutorial for installation using Anaconda.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Next, install the IJulia module in your Julia environment. Remember to press the ] key to open the Package Manager Session:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"julia>\n\n(@v1.9) pkg> add IJulia","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"For this example, make sure to install KomaMRICore and KomaMRIPlots (we do not use KomaMRI directly since we won't be utilizing the KomaUI() function):","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"(@v1.9) pkg> add KomaMRICore\n\n(@v1.9) pkg> add KomaMRIPlots","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Next, open Jupyter, navigate to a working folder, and create a new notebook by clicking on New, then Julia 1.9.3.\"","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"
","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"A new, empty notebook will appear:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"
","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Proceed to write and execute the provided example:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"View code","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"# Import modules\nusing KomaMRICore, KomaMRIPlots\n\n# Define sequence\nampRF = 2e-6 # 2 uT RF amplitude\ndurRF = π / 2 / (2π * γ * ampRF) # required duration for a 90 deg RF pulse\nexc = RF(ampRF, durRF)\n\nnADC = 8192 # number of acquisition samples\ndurADC = 250e-3 # duration of the acquisition\ndelay = 1e-3 # small delay\nacq = ADC(nADC, durADC, delay)\n\nseq = Sequence() # empty sequence\nseq += exc # adding RF-only block\nseq += acq # adding ADC-only block\n\n# Plot the sequence\nplot_seq(seq; slider=false, height=300)","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"
","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"This should be sufficient, and now you can start working with KomaMRI using Jupyter notebooks.","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"If you encounter the issue of WebIO not being detected:","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"
","category":"page"},{"location":"ways-of-using-koma/","page":"Ways of using Koma","title":"Ways of using Koma","text":"Refer to this troubleshooting guide for details. Essentially, you need to install a WebIO extension based on your Jupyter installation.","category":"page"},{"location":"notebooks/#Notebooks","page":"Notebooks","title":"Notebooks","text":"","category":"section"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"You can use KomaMRI with popular programming environments such as Pluto and Jupyter notebooks. The following sections show how to set up these notebooks and test KomaMRI with them.","category":"page"},{"location":"notebooks/#Using-KomaMRI-with-Pluto","page":"Notebooks","title":"Using KomaMRI with Pluto","text":"","category":"section"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"First, install the Pluto module in your Julia environment. Remember to press the ] button to open the Package Manager Session:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"julia>\n\n@(1.9) pkg> add Pluto","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Afterward, return to the Julia Session by pressing the backspace button, and then execute the Pluto.run() function:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"julia> using Pluto\n\njulia> Pluto.run()","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"This should automatically open the Pluto dashboard in your default web browser:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"
","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Next, create a new notebook by clicking on + Create a new notebook:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"
","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Write and run the following code, which is identical to the Free Induction Decay example. Pluto automatically installs the required modules if they are not present on your system. Additionally, note that we do not directly use KomaMRI since we won't be utilizing the KomaUI() function. Instead, we rely on the KomaMRICore and KomaMRIPlots dependencies. To display plots in Pluto, ensure that you import the PlutoPlotly package, as KomaMRIPlots requires its backend to display figures in Pluto:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"One of the most outstanding features of Pluto is its ability to ensure reproducibility by storing the information necessary to recreate the package environment in the notebook file. When others open your notebook with Pluto, it automatically ensures they use the exact same package environment, guaranteeing seamless functionality on their computers.","category":"page"},{"location":"notebooks/#Using-KomaMRI-with-Jupyter","page":"Notebooks","title":"Using KomaMRI with Jupyter","text":"","category":"section"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Ensure you have Jupyter installed on your computer. Follow this tutorial for installation using Anaconda.","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Next, install the IJulia module in your Julia environment. Remember to press the ] key to open the Package Manager Session:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"julia>\n\n(@v1.9) pkg> add IJulia","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"For this example, make sure to install KomaMRICore and KomaMRIPlots (we do not use KomaMRI directly since we won't be utilizing the KomaUI() function):","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"(@v1.9) pkg> add KomaMRICore\n\n(@v1.9) pkg> add KomaMRIPlots","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Next, open Jupyter, navigate to a working folder, and create a new notebook by clicking on New, then Julia 1.9.3.\"","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"
","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"A new, empty notebook will appear:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"
","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Proceed to write and execute the provided example:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"View code","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"# Import modules\nusing KomaMRICore, KomaMRIPlots\n\n# Define sequence\nampRF = 2e-6 # 2 uT RF amplitude\ndurRF = π / 2 / (2π * γ * ampRF) # required duration for a 90 deg RF pulse\nexc = RF(ampRF, durRF)\n\nnADC = 8192 # number of acquisition samples\ndurADC = 250e-3 # duration of the acquisition\ndelay = 1e-3 # small delay\nacq = ADC(nADC, durADC, delay)\n\nseq = Sequence() # empty sequence\nseq += exc # adding RF-only block\nseq += acq # adding ADC-only block\n\n# Plot the sequence\nplot_seq(seq; slider=false, height=300)","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"
","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"This should be sufficient, and now you can start working with KomaMRI using Jupyter notebooks.","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"If you encounter the issue of WebIO not being detected:","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"
","category":"page"},{"location":"notebooks/","page":"Notebooks","title":"Notebooks","text":"Refer to this IJulia documentation and this troubleshooting guide for details. Essentially, you need to install a WebIO extension depending on your Jupyter installation.","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"EditURL = \"../../../../examples/literate/examples/04-3DSliceSelective.jl\"","category":"page"},{"location":"generated/examples/04-3DSliceSelective/#Slice-Selective-Acquisition-of-3D-Phantom","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"","category":"section"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"(Image: ) (Image: )","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"using KomaMRI # hide\nsys = Scanner() # hide","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"While in the previous examples we simulated using hard RF pulses, in this demonstration we will illustrate the principles of slice selection. First, let's import a 3D phantom, in this case a brain slab (thickness of 2mathrmcm), by calling the function brain_phantom3D.","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"obj = brain_phantom3D()\nobj.Δw .= 0 # Removes the off-resonance\np1 = plot_phantom_map(obj, :T2 ; height=400)\nsavefig(p1, \"../../assets/examples/3-phantom.html\") # hide","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"
","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"Now, we are going to import a sequence which acquires 3 slices in the longitudinal axis. Note that the sequence contains three EPIs to acquire 3 slices of the phantom.","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/epi_multislice.seq\")\nseq = read_seq(seq_file)\np2 = plot_seq(seq; range=[0,10], height=400)\nsavefig(p2, \"../../assets/examples/3-seq.html\") # hide","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"We can take a look to the slice profiles by using the function simulate_slice_profile:","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"z = range(-2., 2., 200) * 1e-2; # -2 to 2 cm\nrf1, rf2, rf3 = findall(is_RF_on.(seq))\nM1 = simulate_slice_profile(seq[rf1]; z)\nM2 = simulate_slice_profile(seq[rf2]; z)\nM3 = simulate_slice_profile(seq[rf3]; z)\n\nusing PlotlyJS # hide\npa = scatter(x=z*1e2, y=abs.(M1.xy), name=\"Slice 1\") # hide\npb = scatter(x=z*1e2, y=abs.(M2.xy), name=\"Slice 2\") # hide\npc = scatter(x=z*1e2, y=abs.(M3.xy), name=\"Slice 3\") # hide\npd = plot([pa,pb,pc], Layout(xaxis=attr(title=\"z [cm]\"), height=300,margin=attr(t=40,l=0,r=0), title=\"Slice profiles for the slice-selective sequence\")) # hide\nsavefig(pd, \"../../assets/examples/3-profile.html\") # hide","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"Now let's simulate the acquisition. Notice the three echoes, one for every slice excitation.","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"raw = simulate(obj, seq, sys; sim_params=Dict{String,Any}(\"Nblocks\"=>20))\np3 = plot_signal(raw; slider=false, height=300)\nsavefig(p3, \"../../assets/examples/3-signal.html\") # hide","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"Finally, we reconstruct the acquiered images.","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"# Get the acquisition data\nacq = AcquisitionData(raw)\n\n# Setting up the reconstruction parameters and perform reconstruction\nNx, Ny = raw.params[\"reconSize\"][1:2]\nreconParams = Dict{Symbol,Any}(:reco=>\"direct\", :reconSize=>(Nx, Ny))\nimage = reconstruction(acq, reconParams)\n\n# Plotting the slices\np4 = plot_image(abs.(image[:, :, 1]); height=360, title=\"Slice 1\")\np5 = plot_image(abs.(image[:, :, 2]); height=360, title=\"Slice 2\")\np6 = plot_image(abs.(image[:, :, 3]); height=360, title=\"Slice 3\")\nsavefig(p4, \"../../assets/examples/3-recon1.html\") # hide\nsavefig(p5, \"../../assets/examples/3-recon2.html\") # hide\nsavefig(p6, \"../../assets/examples/3-recon3.html\") # hide","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"
","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"","category":"page"},{"location":"generated/examples/04-3DSliceSelective/","page":"Slice Selective Acquisition of 3D Phantom","title":"Slice Selective Acquisition of 3D Phantom","text":"This page was generated using Literate.jl.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"EditURL = \"../../../../examples/literate/examples/01-FID.jl\"","category":"page"},{"location":"generated/examples/01-FID/#Free-Induction-Decay","page":"Free Induction Decay","title":"Free Induction Decay","text":"","category":"section"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"(Image: ) (Image: )","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"First of all, let's use the KomaMRI package and define the default scanner.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"using KomaMRI\nsys = Scanner() # default hardware definition","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"The free induction decay is the simplest observable NMR signal. This signal is the one that follows a single tipping RF pulse. To recreate this experiment, we will need to define a Sequence with 2 blocks.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"The first block containing an RF pulse with a flip-angle of 90 deg,","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"ampRF = 2e-6 # 2 uT RF amplitude\ndurRF = π / 2 / (2π * γ * ampRF) # required duration for a 90 deg RF pulse\nexc = RF(ampRF,durRF)","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"and the second block containing the ADC.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"nADC = 8192 # number of acquisition samples\ndurADC = 250e-3 # duration of the acquisition\ndelay = 1e-3 # small delay\nacq = ADC(nADC, durADC, delay)","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"Finally, we concatenate the sequence blocks to create the final sequence.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"seq = Sequence() # empty sequence\nseq += exc # adding RF-only block\nseq += acq # adding ADC-only block\np1 = plot_seq(seq; slider=false, height=300)\nsavefig(p1, \"../../assets/examples/1-seq.html\") # hide","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"Now, we will define a Phantom with a single spin at x=0 with T_1=1000mathrmms and T_2=100mathrmms.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"obj = Phantom{Float64}(x=[0.], T1=[1000e-3], T2=[100e-3])","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"Finally, to simulate we will need to use the function simulate.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"raw = simulate(obj, seq, sys)","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"To plot the results we will need to use the plot_signal function","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"p2 = plot_signal(raw; slider=false, height=300)\nsavefig(p2, \"../../assets/examples/1-signal.html\") # hide","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"Nice!, we can see that S(t) follows an exponential decay exp(-tT_2) as expected.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"For a little bit of spiciness, let's add off-resonance to our example. We will use Delta f=-100mathrmHz. For this, we will need to add a definition for Δw in our Phantom","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"obj = Phantom{Float64}(x=[0.], T1=[1000e-3], T2=[100e-3], Δw=[-2π*100])# and simulate again.\n\nraw = simulate(obj, seq, sys)\np3 = plot_signal(raw; slider=false, height=300)\nsavefig(p3, \"../../assets/examples/1-signal2.html\") # hide","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"The signal now follows an exponential of the form exp(-tT_2)cdotexp(-iDeltaomega t). The addition of exp(-iDeltaomega t) to the signal will generate a shift in the image space (Fourier shifting property). This effect will be better visualized and explained in later examples.","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"","category":"page"},{"location":"generated/examples/01-FID/","page":"Free Induction Decay","title":"Free Induction Decay","text":"This page was generated using Literate.jl.","category":"page"},{"location":"mri-theory-informal/#MRI-Theory","page":"MRI Theory","title":"MRI Theory","text":"","category":"section"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"This section is meant to be a general overview or summary of the main MRI concepts and insights. It is a good starting point to show up the most relevant components involved and how they are related for raw signal acquisition and image reconstruction. The idea is to have a fresh and clear understanding of what is happening behind the scenes when using the KomaMRI.jl package. Some light background in Differential Equations, Signal Processing and Fourier Theory is advisable to follow along. ","category":"page"},{"location":"mri-theory-informal/#Raw-Signal-Generation","page":"MRI Theory","title":"Raw Signal Generation","text":"","category":"section"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"In order to generate an image from a phantom object with a scanner system and sequence signals, its necessary to acquire a raw signal s(t). This signal can be though as the sum of all spin magnetizations M of the object:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"s(t) =\nint_x int_y int_z\nunderbrace\nM(x y z t)\n_approx alpha image(xyz) \n mathrmdz mathrmdy mathrmdx","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Note that the magnitude of the magnetization is kind of proportional to the image. In real life it's not possible to get directly all the spin magnetizations, however it's possible to obtain the sum of all of them in the raw signal. To avoid losing image information in the sum operation, every spin magnetization resonates with different Larmor frequencies which values depend on the position (xyz) (i.e. modulated or encoded with the basis of the spatial frequency domain). Thus the raw signal can be thought as:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"beginalign tag1\n\ns(t) approx\nint_x int_y int_z\nunderbrace\nm(x y z)\n_alpha image(xyz)\nunderbrace\ne^-j 2 pi k_x(t) x + k_y(t) y + k_z(t) z\n_modulation basis \n mathrmdz mathrmdy mathrmdx\n\nendalign","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"where:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"veck(t) =\nbeginpmatrix\nk_x(t) \nk_y(t) \nk_z(t)\nendpmatrix =\nfrac2pigamma\nbeginpmatrix\nint_0^t G_x(tau) mathrmd tau\nint_0^t G_y(tau) mathrmd tau\nint_0^t G_z(tau) mathrmd tau\nendpmatrix\n \nbeginmatrix*l\ngamma gyromagnetic ratio \nG_i(t) input gradient signals\nendmatrix*","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"In the above expressions, we can see that the frequency of each spin can be manipulated by applying input gradient signals (or a gradient field). In practice, this gradient field is applied in the longitudinal axis hatz (but it is always dependent on the (xyz) position), which makes the spins able to resonate (or precess) at different Larmor frequencies after another input RF pulse excite them in the transverse direction. Both inputs, the gradient signal and the RF pulse, are part of the effective magnetic field vecB(t):","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"vecB(t) = \nbeginpmatrix\nB_1x(t) \nB_1y(t) \nG_x(t) x + G_y(t) y + G_z(t) z\nendpmatrix\n \nbeginmatrix*l\nB_1i(t) input RF pulse (transverse) \nG_i(t) input gradients (longitudinal)\nendmatrix*","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"It's important to highlight that the coil that senses the raw signal can only detects magnetization components oriented in the transverse direction. For this reason is necessary to apply the short RF signal orthogonally to the longitudinal hatz axe.","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"One of the primary concerns, to generate an image is to design proper input signals for the effective magnetic field vecB(t). In particular, by inspecting equation (1), it's possible to manipulate the spacial frequencies k_x(t), k_y(t) and k_z(t) by applying the gradients G_x(t), G_y(t) and G_z(t). Thus, we have information of the raw signal s(t) an the basis e^-j 2 pi k_x(t) x + k_y(t) y + k_z(t) z. Mathematically speaking, every sample of the raw signal is the Fourier transform of the magnetization m(xyz) for a specific point of the spacial frequency domain:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"s(t) = Fourierm (k_x(t) k_y(t) k_z(t))","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Therefore, to get the magnetization m(xyz) for all the points in the spacial domain its necessary to solve the inverse problem with enough points to cover the complete spacial frequency domain, which can be achieved by following a trajectory over time applying different gradient signals (i.e. a trajectory to complete the k-space).","category":"page"},{"location":"mri-theory-informal/#K-Space-and-Acquisition","page":"MRI Theory","title":"K-Space and Acquisition","text":"","category":"section"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Note that the trajectory to cover the k-space eventually can have any continuos shape, however it cannot fill the complete space. Furthermore, due to natural hardware restrictions, the continuos trajectory is sampled during the acquisition of the raw signal st. Thus, every discrete point of st represents a discrete point in the k-space.","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Intuitively, it is desirable to get many points as possible and homogeneously distributed in the k-space. In particular, since the theory behind the raw signal generation is intimately related with the Fourier Transform, a natural way to cover the k-space is by taken a discrete mesh grid of points (trajectories and samples separated by small cubes). In this case, it is possible to apply Fourier theory to define the minimal k-space resolution (separation of the samples in the k-space) to set space dimensions (Field of Views) and prevent aliasing in the image, and define maximal limits in the k-space to set space resolution in the image.","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"underbrace\nDelta k_i\n_k-space resolution\nlongrightarrow\nunderbrace\nFOV_i\n_space width bounds ","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"underbrace\nW_k_i\n_k-space width bounds\nlongrightarrow\nunderbrace\nDelta i\n_space resolution ","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"forall i in x y z","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Even though a mesh grid of discrete points is the natural way of thinking to cover the k-space, it is always possible possible to apply more exotic k-space trajectories, which could be helpful, for instance, to reduce the complete acquisition time. Keep in mind though, this fact must be regarded when solving the inverse problem for obtaining the image, for example by applying and interpolation function before taking the inverse Fourier Transform.","category":"page"},{"location":"mri-theory-informal/#Spin-Dynamics","page":"MRI Theory","title":"Spin Dynamics","text":"","category":"section"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"It's important to point out that all the magnetization spins are independent from each other, so we could separate the phantom object into multiple spins and solve the Bloch Equations for every magnetization vector vecM independently:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"fracmathrmd vecMmathrmd t =\n gamma vecM times vecB\n- fracM_x hatx + M_y hatyT_2\n- fracM_z hatx + M_0 hatyT_1","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"or:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"beginalign tag2\n\nfracmathrmdmathrmdt vecM =\nunderbrace\ngamma\nbeginbmatrix\n 0 B_z -B_y \n-B_z 0 B_x \n B_y -B_x 0\nendbmatrix\nvecM\n_textrotation \n-\nunderbrace\nbeginbmatrix\ntfrac1T_2 0 0 \n0 tfrac1T_2 0 \n0 0 tfrac1T_1\nendbmatrix\nvecM\n_textrelaxation \n+\nunderbrace\nbeginbmatrix\n0 \n0 \ntfracM_0T_1\nendbmatrix\n_textsteady-state \n\nendalign","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"beginmatrix*l\ngamma gyromagnetic ratio \nT_2 transverse relaxation time constant \nT_1 longitudinal relaxation time constant\nendmatrix*","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"vecM(t) =\nbeginpmatrix\nM_x(t) \nM_y(t) \nM_z(t)\nendpmatrix\n \nvecB(t) = \nbeginpmatrix\nB_x(t) \nB_y(t) \nB_z(t)\nendpmatrix =\nbeginpmatrix\nB_1x(t) \nB_1y(t) \nG_x(t) x + G_y(t) y + G_z(t) z\nendpmatrix","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"beginmatrix*l\nB_1i(t) input RF pulse (transverse) \nG_i(t) input gradients (longitudinal)\nendmatrix*","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Note that equation (2) can be separated into three parts:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Rotation: governed by the inputs RF pulse and gradient signals. It gives an initial excitation and the oscillatory behavior for different Larmor frequencies, respectively. \nRelaxation: gives the decay behavior (the magnetization envelope) after the excitation of the spins.\nSteady-State: spins points towards the longitudinal direction after a while.","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"Thus, to get the raw signal s(t) it's necessary to solve the Bloch equations (equation (2)) for every spin of the phantom object, then sum up the contributions of all of them and finally consider just the components of the transverse plane:","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"s(t) = s_xy(t)\n \ns_xy(t) = s_x(t) + j s_y(t)","category":"page"},{"location":"mri-theory-informal/","page":"MRI Theory","title":"MRI Theory","text":"beginpmatrix\ns_x(t) \ns_y(t) \ns_z(t)\nendpmatrix =\nint_x int_y int_z\nvecM(x y z t)\n mathrmdz mathrmdy mathrmdx","category":"page"},{"location":"docstrings/#API-Documentation","page":"API Documentation","title":"API Documentation","text":"","category":"section"},{"location":"docstrings/#dataflow-graph","page":"API Documentation","title":"Dataflow Graph","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"(Image: )","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Pages = [\"docstrings.md\"]\nDepth = 3","category":"page"},{"location":"docstrings/#datatypes","page":"API Documentation","title":"DataTypes","text":"","category":"section"},{"location":"docstrings/#Mag","page":"API Documentation","title":"Mag","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Mag\nshow(::IO, ::Mag)\ngetproperty(::Vector{Mag}, ::Symbol)","category":"page"},{"location":"docstrings/#KomaMRICore.Mag","page":"API Documentation","title":"KomaMRICore.Mag","text":"mag = Mag(xy::Complex, z::Real)\n\nThe Magnetization struct.\n\nArguments\n\nxy: (::Complex{Float64}) magnetization of a spin in the xy plane\nz: (::Real) magnetization of a spin in the z plane\n\nReturns\n\nmag: (::Mag) Magnetization struct\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#Phantom","page":"API Documentation","title":"Phantom","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.Phantom — Type\nKomaMRI.brain_phantom2D — Function\nKomaMRI.brain_phantom3D — Function","category":"page"},{"location":"docstrings/#Scanner","page":"API Documentation","title":"Scanner","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.Scanner — Type","category":"page"},{"location":"docstrings/#Sequence","page":"API Documentation","title":"Sequence","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.Sequence — Type","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"show(::IO, ::Sequence)\nKomaMRI.is_ADC_on\nKomaMRI.is_RF_on\nKomaMRI.is_GR_on\nKomaMRI.is_Gx_on\nKomaMRI.is_Gy_on\nKomaMRI.is_Gz_on\nKomaMRI.is_Delay\nKomaMRI.durs(::Sequence)\ndur(::Sequence)\nKomaMRI.⏢\nKomaMRI.get_grads\nKomaMRI.get_rfs\nKomaMRI.get_flip_angles\nKomaMRI.get_ADC_on\nKomaMRI.get_kspace\nKomaMRI.get_RF_types\n\nKomaMRI.δ2N","category":"page"},{"location":"docstrings/#Base.show-Tuple{IO, Sequence}","page":"API Documentation","title":"Base.show","text":"str = show(io::IO, s::Sequence)\n\nDisplays information about the Sequence struct s in the julia REPL.\n\nArguments\n\ns: (::Sequence) Sequence struct\n\nReturns\n\nstr (::String) output string message\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#KomaMRICore.is_ADC_on","page":"API Documentation","title":"KomaMRICore.is_ADC_on","text":"y = is_ADC_on(x::Sequence)\ny = is_ADC_on(x::Sequence, t::Union{Array{Float64,1}, Array{Float64,2}})\n\nTells if the sequence seq has elements with ADC active, or active during time t.\n\nArguments\n\nx: (::Sequence) sequence struct\nt: (::Union{Array{Float64,1}, Array{Float64,2}}, [s]) time to check\n\nReturns\n\ny: (::Bool) boolean that tells whether or not the ADC in the sequence is active\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.is_RF_on","page":"API Documentation","title":"KomaMRICore.is_RF_on","text":"y = is_RF_on(x::Sequence)\ny = is_RF_on(x::Sequence, t::Vector{Float64})\n\nTells if the sequence seq has elements with RF active, or active during time t.\n\nArguments\n\nx: (::Sequence) Sequence struct\nt: (::Vector{Float64}, [s]) time to check\n\nReturns\n\ny: (::Bool) boolean that tells whether or not the RF in the sequence is active\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.is_GR_on","page":"API Documentation","title":"KomaMRICore.is_GR_on","text":"y = is_GR_on(x::Sequence)\n\nTells if the sequence seq has elements with GR active.\n\nArguments\n\nx: (::Sequence) Sequence struct\n\nReturns\n\ny: (::Bool) boolean that tells whether or not the GR in the sequence is active\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.dur-Tuple{Sequence}","page":"API Documentation","title":"KomaMRICore.dur","text":"T = dur(x::Sequence)\n\nThe total duration of the sequence in [s].\n\nArguments\n\nx: (::Sequence) Sequence struct\n\nReturns\n\nT: (::Real, [s]) total duration of the sequence\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#KomaMRICore.get_flip_angles","page":"API Documentation","title":"KomaMRICore.get_flip_angles","text":"y = get_flip_angles(x::Sequence)\n\nReturns all the flip angles of the RF pulses in the sequence x.\n\nArguments\n\nx: (::Sequence) Sequence struct\n\nReturns\n\ny: (::Vector{Float64}, [deg]) flip angles\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.get_kspace","page":"API Documentation","title":"KomaMRICore.get_kspace","text":"kspace, kspace_adc = get_kspace(seq::Sequence; Δt=1)\n\nOutputs the designed k-space trajectory of the Sequence seq.\n\nArguments\n\nseq: (::Sequence) Sequence struct\nΔt: (::Real, =1, [s]) nominal delta time separation between two time samples for ADC acquisition and Gradients\n\nReturns\n\nkspace: (3-column ::Matrix{Float64}) kspace\nkspace_adc: (3-column ::Matrix{Float64}) adc kspace\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#Grad","page":"API Documentation","title":"Grad","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.Grad — Type\nKomaMRI.Grad(::Function, ::Real, ::Int64) — Method","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"rotx\nroty\nrotz\nshow(::IO, ::Grad)\ngetproperty(::Vector{Grad}, ::Symbol)\ndur(::Grad)","category":"page"},{"location":"docstrings/#KomaMRICore.rotx","page":"API Documentation","title":"KomaMRICore.rotx","text":"Rx = rotx(θ::Real)\n\nRotates vector counter-clockwise with respect to the x-axis.\n\nArguments\n\nθ: (::Real, [rad]) rotation angle\n\nReturns\n\nRx: (::Matrix{Int64}) rotation matrix\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.roty","page":"API Documentation","title":"KomaMRICore.roty","text":"Ry = roty(θ::Real)\n\nRotates vector counter-clockwise with respect to the y-axis.\n\nArguments\n\nθ: (::Real, [rad]) rotation angle\n\nReturns\n\nRy: (::Matrix{Int64}) rotation matrix\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.rotz","page":"API Documentation","title":"KomaMRICore.rotz","text":"Rz = rotz(θ::Real)\n\nRotates vector counter-clockwise with respect to the z-axis.\n\nArguments\n\nθ: (::Real, [rad]) rotation angle\n\nReturns\n\nRz: (::Matrix{Int64}) rotation matrix\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#Base.show-Tuple{IO, Grad}","page":"API Documentation","title":"Base.show","text":"str = show(io::IO, x::Grad)\n\nDisplays information about the Grad struct x in the julia REPL.\n\nArguments\n\nx: (::Grad) Grad struct\n\nReturns\n\nstr (::String) output string message\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#Base.getproperty-Tuple{Vector{Grad}, Symbol}","page":"API Documentation","title":"Base.getproperty","text":"y = getproperty(x::Vector{Grad}, f::Symbol)\ny = getproperty(x::Matrix{Grad}, f::Symbol)\n\nOverloads Base.getproperty(). It is meant to access properties of the Grad vector x directly without the need to iterate elementwise.\n\nArguments\n\nx: (::Vector{Grad} or ::Matrix{Grad}) vector or matrix of Grad structs\nf: (::Symbol, opts: [:x, :y, :z, :T, :delay, :rise, :delay, :dur, :A, f]) input symbol that represents a property of the vector or matrix of Grad structs\n\nReturns\n\ny: (::Vector{Any} or ::Matrix{Any}) vector or matrix with the property defined by the symbol f for all elements of the Grad vector or matrix x\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#KomaMRICore.dur-Tuple{Grad}","page":"API Documentation","title":"KomaMRICore.dur","text":"y = dur(x::Grad)\ny = dur(x::Vector{Grad})\n\nDuration time in [s] of Grad struct or Grad array. When the input is a gradient vector, then the duration is the maximum duration of all the elements of the gradient vector.\n\nArguments\n\nx: (::Grad or ::Vector{Grad}) RF struct or RF array\n\nReturns\n\ny: (::Float64, [s]) duration of the RF struct or RF array\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#RF","page":"API Documentation","title":"RF","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.RF — Type","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Spinor\nshow(::IO,::Spinor)\n*(::Spinor, ::Spinor)\nRz\nRy\nRx\nKomaMRI.Rg\nKomaMRI.Rφ\nQ\nabs(::Spinor)\n\nshow(::IO, ::RF)\ngetproperty(::Vector{RF}, ::Symbol)\ndur(::RF)\nKomaMRI.RF_fun\nKomaMRI.get_flip_angle\nKomaMRI.get_RF_center","category":"page"},{"location":"docstrings/#KomaMRICore.Spinor","page":"API Documentation","title":"KomaMRICore.Spinor","text":"spinor = Spinor(α, β)\n\nSpinor(α, β) with Cayley-Klein parameters α and β. Based on \"Introduction to the Shinnar-Le Roux algorithm\", Patrick Le Roux (1995). A spinor is a way to represent 3D rotations, the underlying representation is a 2 X 2 complex unitary matrix (alphabetainmathbbC):\n\nR=leftbeginarraycc\nalpha -beta^*\nbeta alpha^*\nendarrayright\n\nwith alpha^2+beta^2 = 1.\n\nThis later operates on the 2times2 representation of (xyz) as follows V^+ = R V R^*.\n\nArguments\n\nα: (::Complex{Float64}) Cayley-Klein parameter α\nβ: (::Complex{Float64}) Cayley-Klein parameter β\n\nReturns\n\nspinor: (::Spinor) Spinor struct\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#Base.show-Tuple{IO, Spinor}","page":"API Documentation","title":"Base.show","text":"str = show(io::IO, s::Spinor)\n\nDisplays the spinor parameters in the julia REPL.\n\nArguments\n\ns: (::Spinor) Spinor struct\n\nReturns\n\nstr: (::String) output string message\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#Base.:*-Tuple{Spinor, Spinor}","page":"API Documentation","title":"Base.:*","text":"s = *(s1::Spinor, s2::Spinor)\n\nSpinor multiplication identity: (α1,β1)×(α2,β2) = (α1 α2 - β2⋆ β1 , β2 α1 + α2⋆ β1)\n\nArguments\n\ns1: (::Spinor) first spinor struct\ns2: (::Spinor) second spinor struct\n\nReturns\n\ns: (::Spinor) multiplication spinnor identity result\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#KomaMRICore.Rz","page":"API Documentation","title":"KomaMRICore.Rz","text":"s = Rz(φ)\n\nSpinor clockwise rotation matrix with angle φ with respect to z-axis.\n\nArguments\n\nφ: (::Real, [rad]) angle with respect to z-axis\n\nReturns\n\ns: (::Spinor) spinnor struct that represents the Rz rotation matrix\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.Ry","page":"API Documentation","title":"KomaMRICore.Ry","text":"s = Ry(θ)\n\nSpinor clockwise rotation matrix with angle θ with respect to y-axis.\n\nArguments\n\nθ: (::Real, [rad]) angle with respect to y-axis\n\nReturns\n\ns: (::Spinor) spinor struct that represents the Ry rotation matrix\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.Rx","page":"API Documentation","title":"KomaMRICore.Rx","text":"s = Rx(θ)\n\nSpinor clockwise rotation matrix with angle θ with respect to x-axis.\n\nArguments\n\nθ: (::Real, [rad]) angle with respect to x-axis\n\nReturns\n\ns: (::Spinor) spinor struct that represents the Rx rotation matrix\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#KomaMRICore.Q","page":"API Documentation","title":"KomaMRICore.Q","text":"s = Q(φ, nxy, nz)\n\nSpinor rotation matrix. Rotation of φ with respect to the axis of rotation n=(nx, ny, nz).\n\nPauly, J., Le Roux, P., Nishimura, D., & Macovski, A. (1991). Parameter relations for the Shinnar-Le Roux selective excitation pulse design algorithm (NMR imaging). IEEE Transactions on Medical Imaging, 10(1), 53-65. doi:10.1109/42.75611\n\nvarphi=-gammaDelta tsqrtleftB_1right^2+left(boldsymbolGcdotboldsymbolx\nright)^2=-gammaDelta tleftVert boldsymbolBrightVert\n\nboldsymboln=boldsymbolBleftVert boldsymbolBrightVert\n\nArguments\n\nφ: (::Real, [rad]) φ angle\nnxy: (::Real) nxy factor\nnz: (::Real) nz factor\n\nReturns\n\ns: (::Spinor) spinnor struct that represents the Q rotation matrix\n\n\n\n\n\n","category":"function"},{"location":"docstrings/#Base.abs-Tuple{Spinor}","page":"API Documentation","title":"Base.abs","text":"y = abs(s::Spinor)\n\nIt calculates |α|^2 + |β|^2 of the Cayley-Klein parameters.\n\nArguments\n\ns: (::Spinor) spinnor struct\n\nReturns\n\ny: (::Real) result of the abs operator\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#Base.show-Tuple{IO, RF}","page":"API Documentation","title":"Base.show","text":"str = show(io::IO, x::RF)\n\nDisplays information about the RF struct x in the julia REPL.\n\nArguments\n\nx: (::RF) RF struct\n\nReturns\n\nstr: (::String) output string message\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#Base.getproperty-Tuple{Vector{RF}, Symbol}","page":"API Documentation","title":"Base.getproperty","text":"y = getproperty(x::Vector{RF}, f::Symbol)\ny = getproperty(x::Matrix{RF}, f::Symbol)\n\nOverloads Base.getproperty(). It is meant to access properties of the RF vector x directly without the need to iterate elementwise.\n\nArguments\n\nx: (::Vector{RF} or ::Matrix{RF}) vector or matrix of RF structs\nf: (::Symbol, opts: [:A, :Bx, :By, :T, :Δf, :delay and :dur]) input symbol that represents a property of the vector or matrix of RF structs\n\nReturns\n\ny: (::Vector{Any} or ::Matrix{Any}) vector with the property defined by the symbol f for all elements of the RF vector or matrix x\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#KomaMRICore.dur-Tuple{RF}","page":"API Documentation","title":"KomaMRICore.dur","text":"y = dur(x::RF)\ny = dur(x::Array{RF,1})\ny = dur(x::Array{RF,2})\n\nDuration time in [s] of RF struct or RF array.\n\nArguments\n\nx: (::RF or ::Array{RF,1} or ::Array{RF,2}) RF struct or RF array\n\nReturns\n\ny: (::Float64, [s]) duration of the RF struct or RF array\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#ADC","page":"API Documentation","title":"ADC","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.ADC — Type","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"getproperty(::Vector{ADC}, ::Symbol)\nKomaMRI.get_adc_sampling_times\nKomaMRI.get_adc_phase_compensation","category":"page"},{"location":"docstrings/#Base.getproperty-Tuple{Vector{ADC}, Symbol}","page":"API Documentation","title":"Base.getproperty","text":"y = getproperty(x::Vector{ADC}, f::Symbol)\n\nOverloads Base.getproperty(). It is meant to access properties of the ADC vector x directly without the need to iterate elementwise.\n\nArguments\n\nx: (::Vector{ADC}) vector of ADC structs\nf: (::Symbol, opts: [:N, :T, :delay, :Δf, :ϕ, :dur]) input symbol that represents a property of the ADC structs\n\nReturns\n\ny: (::Vector{Any}) vector with the property defined by the f for all elements of the ADC vector x\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#Delay","page":"API Documentation","title":"Delay","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.Delay — Type","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"show(::IO, ::Delay)\n+(::Sequence, ::Delay)","category":"page"},{"location":"docstrings/#Base.show-Tuple{IO, Delay}","page":"API Documentation","title":"Base.show","text":"str = show(io::IO, s::Delay)\n\nDisplays the delay time in m[s] of the delay struct s in the julia REPL.\n\nArguments\n\ns: (::Delay) delay struct\n\nReturns\n\nstr: (::String) output string message\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#Base.:+-Tuple{Sequence, Delay}","page":"API Documentation","title":"Base.:+","text":"seq = +(s::Sequence, d::Delay)\nseq = +(d::Delay, s::Sequence)\n\nAdd a delay to sequence struct. It ultimately affects to the duration of the gradients of a sequence.\n\nArguments\n\ns: (::Sequence) sequence struct\nd: (::Delay) delay struct\n\nReturns\n\nseq: (::Sequence) delayed sequence\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#pulseq","page":"API Documentation","title":"Pulseq.jl","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.read_seq — Function","category":"page"},{"location":"docstrings/#read_Grad","page":"API Documentation","title":"read_Grad","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.read_Grad","category":"page"},{"location":"docstrings/#read_RF","page":"API Documentation","title":"read_RF","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.read_RF","category":"page"},{"location":"docstrings/#read_ADC","page":"API Documentation","title":"read_ADC","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.read_ADC","category":"page"},{"location":"docstrings/#get_block","page":"API Documentation","title":"get_block","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.get_block","category":"page"},{"location":"docstrings/#jemris","page":"API Documentation","title":"JEMRIS.jl","text":"","category":"section"},{"location":"docstrings/#read_phantom_jemris","page":"API Documentation","title":"read_phantom_jemris","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.read_phantom_jemris — Function","category":"page"},{"location":"docstrings/#ismrmrd","page":"API Documentation","title":"ISMRMRD.jl","text":"","category":"section"},{"location":"docstrings/#signal_to_raw_data","page":"API Documentation","title":"signal_to_raw_data","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.signal_to_raw_data — Function","category":"page"},{"location":"docstrings/#pulse-designer","page":"API Documentation","title":"PulseDesigner.jl","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.PulseDesigner — Function\nKomaMRI.PulseDesigner.RF_hard — Function\nKomaMRI.PulseDesigner.EPI — Function\nKomaMRI.PulseDesigner.radial_base — Function","category":"page"},{"location":"docstrings/#key-values-calculation","page":"API Documentation","title":"KeyValuesCalculation.jl","text":"","category":"section"},{"location":"docstrings/#get_theo_A","page":"API Documentation","title":"get_theo_A","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.get_theo_A","category":"page"},{"location":"docstrings/#get_theo_t","page":"API Documentation","title":"get_theo_t","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.get_theo_t","category":"page"},{"location":"docstrings/#get_theo_Gi","page":"API Documentation","title":"get_theo_Gi","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.get_theo_Gi","category":"page"},{"location":"docstrings/#trapezoidal-integration","page":"API Documentation","title":"TrapezoidalIntegration.jl","text":"","category":"section"},{"location":"docstrings/#trapz","page":"API Documentation","title":"trapz","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.trapz","category":"page"},{"location":"docstrings/#cumtrapz","page":"API Documentation","title":"cumtrapz","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.cumtrapz","category":"page"},{"location":"docstrings/#time-step-calculation","page":"API Documentation","title":"TimeStepCalculation.jl","text":"","category":"section"},{"location":"docstrings/#points_from_key_times","page":"API Documentation","title":"points_from_key_times","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.points_from_key_times","category":"page"},{"location":"docstrings/#get_variable_times","page":"API Documentation","title":"get_variable_times","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.get_variable_times","category":"page"},{"location":"docstrings/#get_uniform_times","page":"API Documentation","title":"get_uniform_times","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.get_uniform_times","category":"page"},{"location":"docstrings/#kfoldperm","page":"API Documentation","title":"kfoldperm","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.kfoldperm","category":"page"},{"location":"docstrings/#get_breaks_in_RF_key_points","page":"API Documentation","title":"get_breaks_in_RF_key_points","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.get_breaks_in_RF_key_points","category":"page"},{"location":"docstrings/#simulation-core","page":"API Documentation","title":"SimulationCore.jl","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.simulate — Function\nKomaMRI.simulate_slice_profile — Function","category":"page"},{"location":"docstrings/#print_gpus","page":"API Documentation","title":"print_gpus","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.print_gpus","category":"page"},{"location":"docstrings/#run_spin_precession","page":"API Documentation","title":"run_spin_precession","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.run_spin_precession","category":"page"},{"location":"docstrings/#run_spin_precession_parallel","page":"API Documentation","title":"run_spin_precession_parallel","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.run_spin_precession_parallel","category":"page"},{"location":"docstrings/#run_spin_excitation","page":"API Documentation","title":"run_spin_excitation","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.run_spin_excitation","category":"page"},{"location":"docstrings/#run_spin_excitation_parallel","page":"API Documentation","title":"run_spin_excitation_parallel","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.run_spin_excitation_parallel","category":"page"},{"location":"docstrings/#run_sim_time_iter","page":"API Documentation","title":"run_sim_time_iter","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.run_sim_time_iter","category":"page"},{"location":"docstrings/#display-functions","page":"API Documentation","title":"DisplayFunctions.jl","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"Refer to API Documentation:","category":"page"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.plot_seq — Function\nKomaMRI.plot_image — Function\nplot_kspace — Function\nplot_M0 — Function\nplot_phantom_map — Function\nplot_signal — Function","category":"page"},{"location":"docstrings/#theme_chooser","page":"API Documentation","title":"theme_chooser","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.theme_chooser","category":"page"},{"location":"docstrings/#interp_map","page":"API Documentation","title":"interp_map","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.interp_map","category":"page"},{"location":"docstrings/#plot_dict","page":"API Documentation","title":"plot_dict","text":"","category":"section"},{"location":"docstrings/","page":"API Documentation","title":"API Documentation","text":"KomaMRI.plot_dict","category":"page"},{"location":"docstrings/#KomaMRIPlots.plot_dict","page":"API Documentation","title":"KomaMRIPlots.plot_dict","text":"str = plot_dict(dict::Dict)\n\nGenerates an HTML table based on the dictionary dict.\n\nArguments\n\ndict: (::Dict) dictionary\n\nReturns\n\nstr: (::String) dictionary as an HTML table\n\n\n\n\n\n","category":"function"},{"location":"getting-started/#Getting-Started","page":"Getting Started","title":"Getting Started","text":"","category":"section"},{"location":"getting-started/#Installing-Julia","page":"Getting Started","title":"Installing Julia","text":"","category":"section"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"KomaMRI was written in Julia, so the first thing you should do is to install it! The latest version of Julia can be downloaded at the Julia Downloads page. It is advisable you add julia to the PATH, which can be done during the installation process.","category":"page"},{"location":"getting-started/#Installing-KomaMRI","page":"Getting Started","title":"Installing KomaMRI","text":"","category":"section"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"Once Julia is installed, open the Julia REPL, and add the KomaMRI package by typing the following commands:","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"Press the ] key and then press enter to bring up Julia's package manager.\nType add KomaMRI and then press enter in the package manager session.","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"This process should take about 5 minutes in a fresh Julia installation. Here is how it looks in the Julia REPL:","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"julia> ]\n\n(@v1.9) pkg> add KomaMRI","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"Then press Ctrl+C or backspace to return to the julia> prompt.","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"","category":"page"},{"location":"getting-started/#My-First-MRI-Simulation","page":"Getting Started","title":"My First MRI Simulation","text":"","category":"section"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"For our first simulation we will use KomaMRI's graphical user interface (GUI). For this, you will first need to load KomaMRI by typing using KomaMRI, and then launch the GUI with KomaUI().","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"julia> using KomaMRI\n\njulia> KomaUI()","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"The first time you use this command it may take more time than usual, but a window with the Koma GUI will pop up:","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"(Image: )","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"The user interface has some basic definitions for the scanner, phantom, and sequence already preloaded. So you can immediately interact with the simulation and reconstruction processes, and then visualize the results.","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"As a simple demonstration, press the Simulate! button and wait until the simulation is ready. Now you have acquired the Raw Signal and you should see the following:","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"(Image: )","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"Then, press the Reconstruct! button and wait until the reconstruction ends. Now you have reconstructed an Image from the Raw Signal and you should see the following in the GUI:","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"(Image: )","category":"page"},{"location":"getting-started/","page":"Getting Started","title":"Getting Started","text":"Congratulations, you successfully simulated an MRI acquisition! 🎊","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"EditURL = \"../../../../examples/literate/examples/02-SmallTipApproximation.jl\"","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/#Small-Tip-Angle-Approximation","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"","category":"section"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"(Image: ) (Image: )","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"Based on the results in page 41 of the book \"Handbook of MRI Pulse Sequences\" by Bernstein et al.","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"using KomaMRI # hide\nsys = Scanner() # hide\nsys.Smax = 50 # hide","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"In this example, we will showcase a common approximation in MRI, the small tip angle approximation. For this, we will simulate a slice profile for spins with positions zin-22mathrmcm and with a gradient G_z so their frequencies are mapped to fin-55mathrmkHz. To start, we define an RF pulse with a flip angle of 30 deg and pulse duration of T_mathrmrf=32mathrmms.","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"B1 = 4.92e-6\nTrf = 3.2e-3\nzmax = 2e-2\nfmax = 5e3\nz = range(-zmax, zmax, 400)\nGz = fmax / (γ * zmax)\nf = γ * Gz * z # hide","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"The designed RF pulse is presented in the figure below, where the additional gradient refocuses the spins' phase after the excitation.","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"seq = PulseDesigner.RF_sinc(B1, Trf, sys; G=[0;0;Gz], TBP=8)\np2 = plot_seq(seq; max_rf_samples=Inf, slider=false)\nsavefig(p2, \"../../assets/examples/42-seq.html\") # hide","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"Now we will perform the simulation using the function simulate_slice_profile. Note that we modified Δt_rf in sim_params to match the resolution of the waveform.","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"sim_params = Dict{String, Any}(\"Δt_rf\" => Trf / length(seq.RF.A[1]))\nM = simulate_slice_profile(seq; z, sim_params)\n\nusing PlotlyJS # hide\ns1 = scatter(x=f, y=real.(M.xy), name=\"Mx\") # hide\ns2 = scatter(x=f, y=imag.(M.xy), name=\"My\") # hide\ndat = seq.RF.A[1] # hide\nN = length(dat) # hide\ndat_pad = [zeros(floor(Int64,N)); dat; zeros(floor(Int64,N))] # hide\nN_pad = length(dat_pad) # hide\nU = 1 / (Trf) * N / N_pad #hide\nu = range(0, (N_pad - 1) * U; step=U) # hide\nu = u .- maximum(u) / 2 .- U/2 # hide\nFT_dat_pad = abs.(KomaMRI.fftc(dat_pad; dims=1)) # hide\nscale_factor = maximum(abs.(M.xy)) / maximum(FT_dat_pad) # hide\ns3 = scatter(x=u, y=FT_dat_pad*scale_factor, name=\"|FT(B₁(t))|\", line=attr(dash=\"dash\")) # hide\npb = plot([s1,s2,s3], Layout(title=\"30 deg SINC pulse (TBP=8, Hamming)\", xaxis_title=\"Frequency [Hz]\", xaxis_range=[-fmax,fmax])) # hide\nsavefig(pb, \"../../assets/examples/4b-profile.html\") # hide","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"This produces the following slice profile:","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"As you can see, for a flip angle of 30 deg, the slice profile is very close to the small tip angle approximation (the Fourier transform of B_1(t)).","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"But what will happen if we use a flip angle of 120 deg instead?","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"α_desired = 120 + 0im # The multiplication of a complex number scales the RF pulse of a Sequence\nα = get_flip_angles(seq)[1] # Previous FA approx 30 deg\nseq = (α_desired / α) * seq # Scaling the pulse to have a flip angle of 120\nM = simulate_slice_profile(seq; z, sim_params)\n\ns1 = scatter(x=f, y=abs.(M.xy), name=\"|Mxy|\") # hide\ndat = seq.RF.A[1] # hide\nN = length(dat) # hide\ndat_pad = [zeros(floor(Int64,N)); dat; zeros(floor(Int64,N))] # hide\nN_pad = length(dat_pad) # hide\nU = 1 / (Trf) * N / N_pad #hide\nu = range(0, (N_pad - 1) * U; step=U) # hide\nu = u .- maximum(u) / 2 .- U/2 # hide\nFT_dat_pad = abs.(KomaMRI.fftc(dat_pad; dims=1)) # hide\nscale_factor = maximum(abs.(M.xy)) / maximum(FT_dat_pad) # hide\ns2 = scatter(x=u, y=FT_dat_pad*scale_factor, name=\"|FT(B₁(t))|\", line=attr(dash=\"dash\")) # hide\npa = plot([s1,s2], Layout(title=\"120 deg SINC pulse (TBP=8, Hamming)\", xaxis_title=\"Frequency [Hz]\", xaxis_range=[-fmax,fmax])) # hide\nsavefig(pa, \"../../assets/examples/4a-profile.html\") # hide","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"For this case, the small tip angle approximation breaks 😢, thus, the reason for its name!","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"This basic sinc pulse is not designed to be B_1-insensitive. Some adiabatic RF pulses have been proposed to achieve this. Watch out for a future example showing these adiabatic RF pulses 👀.","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"","category":"page"},{"location":"generated/examples/02-SmallTipApproximation/","page":"Small Tip Angle Approximation","title":"Small Tip Angle Approximation","text":"This page was generated using Literate.jl.","category":"page"},{"location":"create-your-own-sequence/#Create-Your-Own-Sequence","page":"Create Your Own Sequence","title":"Create Your Own Sequence","text":"","category":"section"},{"location":"create-your-own-sequence/","page":"Create Your Own Sequence","title":"Create Your Own Sequence","text":"warning: Warning\nThis section is currently under construction, and some details on how to construct a Sequence may be missing.","category":"page"},{"location":"create-your-own-sequence/","page":"Create Your Own Sequence","title":"Create Your Own Sequence","text":"This is an example of how to create a Sequence struct:","category":"page"},{"location":"create-your-own-sequence/","page":"Create Your Own Sequence","title":"Create Your Own Sequence","text":"# Export necessary modules\nusing KomaMRI\n\n# Create the function that creates a phantom\nfunction sequence_example(FOV::Real, N::Integer)\n\n # Define initial paramters (TODO: consider when N is even)\n sys = Scanner()\n\tΔt = sys.ADC_Δt\n\tGmax = sys.Gmax\n\tNx = Ny = N #Square acquisition\n\tΔx = FOV/(Nx-1)\n\tTa = Δt*(Nx-1) #4-8 us\n Δτ = Ta/(Ny-1)\n\tGa = 1/(γ*Δt*FOV)\n\tζ = Ga / sys.Smax\n\tGa ≥ sys.Gmax ? error(\"Ga=$(Ga*1e3) mT/m exceeds Gmax=$(Gmax*1e3) mT/m, increase Δt to at least Δt_min=\"\n\t*string(round(1/(γ*Gmax*FOV),digits=2))*\" us.\") : 0\n\tϵ1 = Δτ/(Δτ+ζ)\n\n\t# EPI base\n\tEPI = Sequence(vcat(\n\t [mod(i,2)==0 ? Grad(Ga*(-1)^(i/2),Ta,ζ) : Grad(0.,Δτ,ζ) for i=0:2*Ny-2], #Gx\n\t \t[mod(i,2)==1 ? ϵ1*Grad(Ga,Δτ,ζ) : Grad(0.,Ta,ζ) for i=0:2*Ny-2])) #Gy\n\tEPI.ADC = [mod(i,2)==1 ? ADC(0,Δτ,ζ) : ADC(N,Ta,ζ) for i=0:2*Ny-2]\n\n\t# Pre-wind and wind gradients\n\tϵ2 = Ta/(Ta+ζ)\n PHASE = Sequence(reshape(1/2*[Grad( -Ga, Ta, ζ); ϵ2*Grad(-Ga, Ta, ζ)],:,1)) # This needs to be calculated differently\n\tDEPHASE = Sequence(reshape(1/2*[Grad((-1)^N*Ga, Ta, ζ); ϵ2*Grad(-Ga, Ta, ζ)],:,1)) # for even N\n\tseq = PHASE + EPI + DEPHASE\n\n\t# Saving parameters\n\tseq.DEF = Dict(\"Nx\"=>Nx,\"Ny\"=>Ny,\"Nz\"=>1,\"Name\"=>\"epi\")\n\n # Return the sequence\n\treturn seq\nend\n\n# Call the function to create a sequence\nFOV, N = 23e-2, 101\nseq = sequence_example(FOV, N)\n\n# Plot the sequence in time and its kspace\nplot_seq(seq; range=[0 30])\nplot_kspace(seq)","category":"page"},{"location":"create-your-own-sequence/","page":"Create Your Own Sequence","title":"Create Your Own Sequence","text":"","category":"page"},{"location":"sequence/#Sequence","page":"Sequence","title":"Sequence","text":"","category":"section"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"This section delves into some details about how a sequence is constructed. The sequence definition in KomaMRI is strongly related to the Pulseq definition. After reading this section, you should be able to create your own Sequence structs for conducting custom simulations using the KomaMRI package.","category":"page"},{"location":"sequence/#Sequence-Overview","page":"Sequence","title":"Sequence Overview","text":"","category":"section"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"Let's introduce the following simple sequence figure to expand from a visual example to a more general sequence definition:","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"A sequence can be thought of as an ordered concatenation of blocks over time. Each block is essentially a sequence with a length of 1. Every block consists of an RF pulse, the (xyz) gradients, and the acquisition of samples. Each block also has an associated time duration. To simplify, we will refer to these components as follows:","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"beginmatrix*l\ntextseqi textblock i of the sequence \ntextseqRFi textRF pulse at the i block \ntextseqGRxi textgradient x at the i block \ntextseqGRyi textgradient y at the i block \ntextseqGRzi textgradient z at the i block \ntextseqADCi textacquisition at the i block \ntextseqDURi textduration at the i block\nendmatrix*","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"The best way to understand the Sequence struct in KomaMRI is by examining the source code where this struct is defined:","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"mutable struct Sequence\n GR::Array{Grad,2}\n RF::Array{RF,2}\n ADC::Array{ADC,1}\n DUR::Array{Any,1}\n DEF::Dict{String,Any}\nend","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"As you can see, a Sequence struct contains 5 field names: ''DEF'' contains information for reconstruction steps (so it is not mandatory to fill it), ''DUR'' is a vector that contains the time durations of each block, ''ADC'' is also a vector with the acquisition samples for every block (an vector of ADC structs), ''GR'' is a 2D matrix which 3 rows representing the x-y-z gradients and columns having the samples of each block (a matrix of Grad structs) and ''RF'' is also a 2D matrix where each row represents a different coil and the columns are for different block samples too (a matrix of RF structs). The RF, Grad and ADC are MRI events that will be explained in the section Events Definitions.","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"warning: Warning\nSo far, KomaMRI can only manage one coil for RF excitations. However, in future versions, parallel transmit pTX will be managed by adding more ``rows'' to the RF matrix of the Sequence field name.","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"In order to understand how a Sequence struct can be manipulated in Julia, let's use the EPI sequence example. You can display basic information of the Sequence variable in the Julia REPL:","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"julia> seq = PulseDesigner.EPI_example()\nSequence[ τ = 62.846 ms | blocks: 204 | ADC: 101 | GR: 205 | RF: 1 | DEF: 5 ]","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"As you can see, this Sequence has 204 blocks, 1 of these blocks has an RF struct with values different from zero, there are 205 number of Grad structs considering the x-y-z components, 101 ADC structs acquire samples of some blocks and 62.846 ms is the total time duration of the complete Sequence.","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"To display the sequence in an graph, we can use the plot_seq function:","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"julia> plot_seq(seq; slider=false)","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"This way, you can see exactly where the RF, Grad and ADC structs are located in time.","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"You can access and filter information for the RF, Grad, ADC, and DUR field names of a Sequence using the dot notation. This allows you to display helpful information about the organization of the Sequence struct:","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"julia> seq.RF\n1×204 Matrix{RF}:\n ⊓(0.5872 ms) ⇿(0.0 ms) ⇿(0.0 ms) … ⇿(0.0 ms) ⇿(0.0 ms) \n\njulia> seq.GR\n3×204 Matrix{Grad}:\n ⇿(0.5872 ms) ⊓(0.4042 ms) ⊓(0.4042 ms) … ⇿(0.2062 ms) ⊓(0.4042 ms) ⊓(0.4042 ms)\n ⇿(0.5872 ms) ⊓(0.4042 ms) ⇿(0.4042 ms) ⊓(0.2062 ms) ⇿(0.4042 ms) ⊓(0.4042 ms)\n ⇿(0.5872 ms) ⇿(0.0 ms) ⇿(0.0 ms) ⇿(0.0 ms) ⇿(0.0 ms) ⇿(0.0 ms)\n\njulia> seq.ADC\n204-element Vector{ADC}:\n ADC(0, 0.0, 0.0, 0.0, 0.0)\n ADC(0, 0.0, 0.0, 0.0, 0.0)\n ADC(101, 0.00019999999999999998, 0.00010211565434713023, 0.0, 0.0)\n ⋮\n ADC(101, 0.00019999999999999998, 0.00010211565434713023, 0.0, 0.0)\n ADC(0, 0.0, 0.0, 0.0, 0.0)\n\njulia> seq.DUR\n204-element Vector{Float64}:\n 0.0005871650124959989\n 0.0004042313086942605\n 0.0004042313086942605\n ⋮\n 0.0004042313086942605\n 0.0004042313086942605","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"Additionally, you can access a subset of blocks in a Sequence by slicing or indexing. The result will also be a Sequence struct, allowing you to perform the same operations as you would with a full Sequence. For example, if you want to analyze the first 11 blocks, you can do the following:","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"julia> seq[1:11]\nSequence[ τ = 3.837 ms | blocks: 11 | ADC: 5 | GR: 11 | RF: 1 | DEF: 5 ]\n\njulia> seq[1:11].GR\n3×11 Matrix{Grad}:\n ⇿(0.5872 ms) ⊓(0.4042 ms) ⊓(0.4042 ms) … ⊓(0.4042 ms) ⇿(0.2062 ms) ⊓(0.4042 ms)\n ⇿(0.5872 ms) ⊓(0.4042 ms) ⇿(0.4042 ms) ⇿(0.4042 ms) ⊓(0.2062 ms) ⇿(0.4042 ms)\n ⇿(0.5872 ms) ⇿(0.0 ms) ⇿(0.0 ms) ⇿(0.0 ms) ⇿(0.0 ms) ⇿(0.0 ms)\n\njulia> plot_seq(seq[1:11]; slider=false)","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"","category":"page"},{"location":"sequence/#Concatenation-of-Sequences","page":"Sequence","title":"Concatenation of Sequences","text":"","category":"section"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"Sequences can be concatenated side by side. The example below demonstrates how to concatenate sequences:","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"julia> s = PulseDesigner.EPI_example()[1:11]\nSequence[ τ = 3.837 ms | blocks: 11 | ADC: 5 | GR: 11 | RF: 1 | DEF: 5 ]\n\njulia> seq = s + s + s\nSequence[ τ = 11.512 ms | blocks: 33 | ADC: 15 | GR: 33 | RF: 3 | DEF: 5 ]\n\njulia> plot_seq(seq; slider=false)","category":"page"},{"location":"sequence/","page":"Sequence","title":"Sequence","text":"","category":"page"},{"location":"create-your-own-phantom/#Create-Your-Own-Phantom","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"","category":"section"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"In this section, we will create a custom Phantom struct. While the example is presented in 2D, the concepts discussed here can be readily extended to 3D phantoms.","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"In KomaMRI, the creation of a Phantom struct involves defining spin position arrays (x, y, z) and spin property arrays. The indices of these arrays are then associated with independent spins.","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"For instance, you can create a Phantom with one spin like so:","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"# Define arrays of positions (spin at zero position)\nx = [0.0]\ny = [0.0]\nz = [0.0]\n\n# Define arrays of properties (for CSF tissue)\nρ = [1.0]\nT1 = [2.569]\nT2 = [0.329]\nT2s = [0.058]\n\n# Define the phantom\nspin = Phantom(name=\"spin\", x=x, y=y, z=z, ρ=ρ, T1=T1, T2=T2, T2s=T2s)","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"Phantom{Float64}\n name: String \"spin\"\n x: Array{Float64}((1,)) [0.0]\n y: Array{Float64}((1,)) [0.0]\n z: Array{Float64}((1,)) [0.0]\n ρ: Array{Float64}((1,)) [1.0]\n T1: Array{Float64}((1,)) [2.569]\n T2: Array{Float64}((1,)) [0.329]\n T2s: Array{Float64}((1,)) [0.058]\n Δw: Array{Float64}((1,)) [0.0]\n Dλ1: Array{Float64}((1,)) [0.0]\n Dλ2: Array{Float64}((1,)) [0.0]\n Dθ: Array{Float64}((1,)) [0.0]\n ux: #122 (function of type KomaMRICore.var\"#122#136\")\n uy: #123 (function of type KomaMRICore.var\"#123#137\")\n uz: #124 (function of type KomaMRICore.var\"#124#138\")","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"You can add more properties to the Phantom, such as off-resonance, diffusion parameters, and even functions of motion. However, we won't be utilizing them (except for the off-resonance parameter) to maintain simplicity.","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"If you are familiar with the MRI world, you likely have a 2D or 3D array, where each element contains an ID number identifying a different class of tissue. In this setup, the array axes represent spatial positions, while the elements are used for tissue identification.","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"In this example, we will utilize a .mat file containing arrays with such arrangements. The file is readily available upon installing KomaMRI. Let's read the file and store the 2D data in an array called class:\"","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"# Import necessary modules\nusing KomaMRI, MAT\n\n# Get data from a .mat file\npath_koma = dirname(dirname(pathof(KomaMRI)))\npath_phantom_mat = joinpath(path_koma, \"KomaMRICore\", \"src\", \"datatypes\",\"phantom\", \"brain2D.mat\")\ndata = MAT.matread(path_phantom_mat)\nclass = data[\"axial\"]","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"You can visualize the tissue map using the function plot_image`:","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"plot_image(class)","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"
","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"Let's define the position arrays. You need to know the distance between the spins in the original array (in this case, it is 0.5mm), and then you can determine all the positions like this (the z-component is not calculated since this is a 2D example):","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"# Define spin position arrays\nΔx = .5e-3 # 0.5mm\nM, N = size(class) # Number of spins in x and y\nFOVx = (M-1)*Δx # Field of view in x\nFOVy = (N-1)*Δx # Field of view in y\nx = -FOVx/2:Δx:FOVx/2 # x spin coordinates vector\ny = -FOVy/2:Δx:FOVy/2 # y spin coordinates vector\nx, y = x .+ y'*0, x*0 .+ y' # x and y grid points","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"Now, let's define the arrays for the properties. It's essential to have prior knowledge of the property values for different tissue classes. For example, for muscle tissue, we use ρ = 1, T1 = 900 * 1e-3, T2 = 47 * 1e-3, and T2s = 30 * 1e-3. Additionally, create an array mask to identify the location of a tissue's ID. For muscle with ID = 116, the mask is (class == 116). Finally, to obtain a property, sum all the masks with values for all tissue classes. This process is illustrated below: ","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"# Define the proton density array\nρ = (class.==23)*1 .+ # CSF\n (class.==46)*.86 .+ # GM\n (class.==70)*.77 .+ # WM\n (class.==93)*1 .+ # FAT1\n (class.==116)*1 .+ # MUSCLE\n (class.==139)*.7 .+ # SKIN/MUSCLE\n (class.==162)*0 .+ # SKULL\n (class.==185)*0 .+ # VESSELS\n (class.==209)*.77 .+ # FAT2\n (class.==232)*1 .+ # DURA\n (class.==255)*.77 # MARROW\n\n# Define the T1 decay array\nT1 = (class.==23)*2569 .+ # CSF\n (class.==46)*833 .+ # GM\n (class.==70)*500 .+ # WM\n (class.==93)*350 .+ # FAT1\n (class.==116)*900 .+ # MUSCLE\n (class.==139)*569 .+ # SKIN/MUSCLE\n (class.==162)*0 .+ # SKULL\n (class.==185)*0 .+ # VESSELS\n (class.==209)*500 .+ # FAT2\n (class.==232)*2569 .+ # DURA\n (class.==255)*500 # MARROW\n\n# Define the T2 decay array\nT2 = (class.==23)*329 .+ # CSF\n (class.==46)*83 .+ # GM\n (class.==70)*70 .+ # WM\n (class.==93)*70 .+ # FAT1\n (class.==116)*47 .+ # MUSCLE\n (class.==139)*329 .+ # SKIN/MUSCLE\n (class.==162)*0 .+ # SKULL\n (class.==185)*0 .+ # VESSELS\n (class.==209)*70 .+ # FAT2\n (class.==232)*329 .+ # DURA\n (class.==255)*70 # MARROW\n\n# Define the T2s decay array\nT2s = (class.==23)*58 .+ # CSF\n (class.==46)*69 .+ # GM\n (class.==70)*61 .+ # WM\n (class.==93)*58 .+ # FAT1\n (class.==116)*30 .+ # MUSCLE\n (class.==139)*58 .+ # SKIN/MUSCLE\n (class.==162)*0 .+ # SKULL\n (class.==185)*0 .+ # VESSELS\n (class.==209)*61 .+ # FAT2\n (class.==232)*58 .+ # DURA\n (class.==255)*61 # MARROW\n\n# Define off-resonance array\nΔw_fat = -220*2π\nΔw = (class.==93)*Δw_fat .+ # FAT1\n\t(class.==209)*Δw_fat # FAT2\n\n# Adjust with scaling factor\nT1 = T1*1e-3\nT2 = T2*1e-3\nT2s = T2s*1e-3","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"Finally, we can invoke the Phantom constructor. However, before doing so, we choose not to store spins where the proton density is zero to avoid unnecessary data storage. This is achieved by applying the mask ρ.!=0 to the arrays. Additionally, please note that we set the z-position array filled with zeros, and we interchange the x and y coordinates.\"","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"# Define the phantom\nobj = Phantom{Float64}(\n name = \"custom-brain\",\n\tx = y[ρ.!=0],\n\ty = x[ρ.!=0],\n\tz = 0*x[ρ.!=0],\n\tρ = ρ[ρ.!=0],\n\tT1 = T1[ρ.!=0],\n\tT2 = T2[ρ.!=0],\n\tT2s = T2s[ρ.!=0],\n\tΔw = Δw[ρ.!=0],\n)","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"We can display the Phantom struct with the plot_phantom_map function. In this case we select the proton density to be displayed, but you can choose other property to be displayed:","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"plot_phantom_map(obj, :ρ)","category":"page"},{"location":"create-your-own-phantom/","page":"Create Your Own Phantom","title":"Create Your Own Phantom","text":"","category":"page"},{"location":"api/#API-Documentation","page":"API Documentation","title":"API Documentation","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"This page provides documentation for the modules, structs, functions, methods, and additional components available when importing the KomaMRI package. It serves as a valuable reference when using the Julia REPL directly and when creating custom Julia scripts. Be sure not to overlook the section How to read the API docs, which contains important information for understanding the general structure of docstrings. The following is the table of contents for the API Documentation:","category":"page"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"Pages = [\"api.md\"]\nDepth = 3","category":"page"},{"location":"api/#How-to-read-the-API-docs","page":"API Documentation","title":"How to read the API docs","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"The API documentation includes predefined \"template patterns\" to assist users in understanding how to use modules, structs, functions, methods, and all the necessary aspects to make the most of what KomaMRI has to offer.","category":"page"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"These documentation \"template patterns\" are based on the JJulia Blue Style documentation and other GitHub repositories that deal with MRI topics. However, some custom considerations were added to enhance understanding and provide a broader perspective.","category":"page"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"When you encounter a docstring documentation, it will have the following structure:","category":"page"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"docstring: KomaMRI.component_name — Component\nout1, out2, ... = component_name(arg1, arg2, ...; kw1, kw2, ...)This is a brief description of what component_name does.note: Note\nHere can be placed a note if it is regarded necessary.Argumentsarg1: (::type, =value, [unit], opts: [opt1, opt2, ...]) the description for the arg1\n...Keywordskw1: (::type, =value, [unit], opts: [opt1, opt2, ...]) the description for the kw1\n...Returnsout1: (::type, =value, [unit], opts: [opt1, opt2, ...]) the description for the out1\n...ReferencesSometimes it is a good idea to put some references or links\n...Examplesjulia> arg1, arg2, valkw1, valkw2 = 3.5, \"hello\", 1, true\n\njulia> out1, out2 = component_name(arg1, arg2; kw1=valkw1, kw2=valkw2)","category":"page"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"The preceding docstring block will always start with the way the component is called (outputs = component_name(inputs), followed by a brief description of what the component does. If necessary, a note block will be displayed. In general, the following subsections are optional: Arguments, Keywords, Returns, References, and Examples, but they will be provided as needed. These subsections are self-explanatory, making it intuitive to understand their purpose.","category":"page"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"Please note that every subitem in the sections Arguments, Keywords, and Returns represents variables. They include practical information along with a description. The information enclosed in parentheses is optional but highly useful when provided.","category":"page"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"::type: is the suggested type for the variable. If the input variable is of type ::type, there won't be any issues, but it's always possible to test other subtypes. If the variable is an output, it will be forced to the type ::type whenever possible.\n=value: sometimes, for the inputs, a default value is defined if it is not assigned by the user.\n[unit]: this is the suggested physical unit of measure for the variable. Everything will be fine if you stick with these units of measure.\nopts: [opt1, opt2, ...]: sometimes, the input value can only be interpreted if it is one of the predefined values.","category":"page"},{"location":"api/#Structs","page":"API Documentation","title":"Structs","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"CurrentModule = KomaMRICore","category":"page"},{"location":"api/#Scanner","page":"API Documentation","title":"Scanner","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"Scanner","category":"page"},{"location":"api/#KomaMRICore.Scanner","page":"API Documentation","title":"KomaMRICore.Scanner","text":"sys = Scanner(B0, B1, Gmax, Smax, ADC_Δt, seq_Δt, GR_Δt, RF_Δt,\n RF_ring_down_T, RF_dead_time_T, ADC_dead_time_T)\n\nThe Scanner struct.\n\nArguments\n\nB0: (::Real, =1.5, [T]) main magnetic field strength\nB1: (::Real, =10e-6, [T]) maximum RF amplitude\nGmax: (::Real, =60e-3, [T/m]) maximum gradient amplitude\nSmax: (::Real, =500, [mT/m/ms]) gradient maximum slew-rate\nADC_Δt: (::Real, =2e-6, [s]) ADC raster time\nseq_Δt: (::Real, =1e-5, [s]) sequence-block raster time\nGR_Δt: (::Real, =1e-5, [s]) gradient raster time\nRF_Δt: (::Real, =1e-6, [s]) RF raster time\nRF_ring_down_T: (::Real, =20e-6, [s]) RF ring down time\nRF_dead_time_T: (::Real, =100e-6, [s]) RF dead time\nADC_dead_time_T: (::Real, =10e-6, [s]) ADC dead time\n\nReturns\n\nsys: (::Scanner) Scanner struct\n\nExamples\n\njulia> sys = Scanner()\n\njulia> sys.B0\n\n\n\n\n\n","category":"type"},{"location":"api/#Phantom","page":"API Documentation","title":"Phantom","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"Phantom\nbrain_phantom2D\nbrain_phantom3D","category":"page"},{"location":"api/#KomaMRICore.Phantom","page":"API Documentation","title":"KomaMRICore.Phantom","text":"obj = Phantom(name, x, y, z, ρ, T1, T2, T2s, Δw, Dλ1, Dλ2, Dθ, ux, uy, uz)\n\nThe Phantom struct.\n\nArguments\n\nname: (::String) name of the phantom\nx: (::AbstractVector{T}, [m]) vector of x-positions of the spins\ny: (::AbstractVector{T}, [m]) vector of y-positions of the spins\nz: (::AbstractVector{T}, [m]) vector of z-positions of the spins\nρ: (::AbstractVector{T}) vector of proton density of the spins\nT1: (::AbstractVector{T}, [s]) vector of T1 parameters of the spins\nT2: (::AbstractVector{T}, [s]) vector of T2 parameters of the spins\nT2s: (::AbstractVector{T}, [s]) vector of T2s parameters of the spins\nΔw: (::AbstractVector{T}, [rad/s]) vector of off-resonance parameters of the spins\nDλ1: (::AbstractVector{T}) vector of Dλ1 (diffusion) parameters of the spins\nDλ2: (::AbstractVector{T}) vector of Dλ2 (diffusion) parameters of the spins\nDθ: (::AbstractVector{T}) vector of Dθ (diffusion) parameters of the spins\nux: (::Function) displacement field in the x-axis\nuy: (::Function) displacement field in the y-axis\nuz: (::Function) displacement field in the z-axis\n\nReturns\n\nobj: (::Phantom) Phantom struct\n\nExamples\n\njulia> obj = Phantom()\n\njulia> obj.ρ\n\n\n\n\n\n","category":"type"},{"location":"api/#KomaMRICore.brain_phantom2D","page":"API Documentation","title":"KomaMRICore.brain_phantom2D","text":"phantom = brain_phantom2D(;axis=\"axial\", ss=4)\n\nCreates a two-dimentional brain phantom struct.\n\nReferences\n\nB. Aubert-Broche, D.L. Collins, A.C. Evans: \"A new improved version of the realistic digital brain phantom\" NeuroImage, in review - 2006\nB. Aubert-Broche, M. Griffin, G.B. Pike, A.C. Evans and D.L. Collins: \"20 new digital brain phantoms for creation of validation image data bases\" IEEE TMI, in review - 2006\nhttps://brainweb.bic.mni.mcgill.ca/brainweb\n\nKeywords\n\naxis: (::String, =\"axial\", opts=[\"axial\"]) orientation of the phantom\nss: (::Real, =4) subsampling parameter in all axis\n\nReturns\n\nphantom: (::Phantom) 2D Phantom struct\n\nExamples\n\njulia> obj = brain_phantom2D()\n\njulia> plot_phantom_map(obj, :ρ)\n\n\n\n\n\n","category":"function"},{"location":"api/#KomaMRICore.brain_phantom3D","page":"API Documentation","title":"KomaMRICore.brain_phantom3D","text":"phantom = brain_phantom3D(;ss=4)\n\nCreates a three-dimentional brain phantom struct.\n\nReferences\n\nB. Aubert-Broche, D.L. Collins, A.C. Evans: \"A new improved version of the realistic digital brain phantom\" NeuroImage, in review - 2006\nB. Aubert-Broche, M. Griffin, G.B. Pike, A.C. Evans and D.L. Collins: \"20 new digital brain phantoms for creation of validation image data bases\" IEEE TMI, in review - 2006\nhttps://brainweb.bic.mni.mcgill.ca/brainweb\n\nKeywords\n\nss: (::Real, =4) subsampling parameter in all axis\n\nReturns\n\nphantom: (::Phantom) 3D Phantom struct\n\nExamples\n\njulia> obj = brain_phantom3D()\n\njulia> plot_phantom_map(obj, :ρ)\n\n\n\n\n\n","category":"function"},{"location":"api/#Sequence","page":"API Documentation","title":"Sequence","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"Sequence","category":"page"},{"location":"api/#KomaMRICore.Sequence","page":"API Documentation","title":"KomaMRICore.Sequence","text":"seq = Sequence()\nseq = Sequence(GR)\nseq = Sequence(GR, RF)\nseq = Sequence(GR, RF, ADC)\nseq = Sequence(GR, RF, ADC, DUR)\nseq = Sequence(GR::Array{Grad,1})\nseq = Sequence(GR::Array{Grad,1}, RF::Array{RF,1})\nseq = Sequence(GR::Array{Grad,1}, RF::Array{RF,1}, A::ADC, DUR, DEF)\n\nThe Sequence struct.\n\nArguments\n\nGR: (::Matrix{Grad}) gradient matrix, rows are for (x,y,z) and columns are for time\nRF: (::Matrix{RF}) RF matrix, the 1 row is for the coil and columns are for time\nADC: (::Vector{ADC}) ADC vector in time\nDUR: (::Vector{Float64}, [s]) duration of each sequence-block, this enables delays after RF pulses to satisfy ring-down times\nDEF: (::Dict{String, Any}) dictionary with relevant information of the sequence. The possible keys are [\"AdcRasterTime\", \"GradientRasterTime\", \"Name\", \"Nz\", \"Num_Blocks\", \"Nx\", \"Ny\", \"PulseqVersion\", \"BlockDurationRaster\", \"FileName\", \"RadiofrequencyRasterTime\"]\n\nReturns\n\nseq: (::Sequence) Sequence struct\n\n\n\n\n\n","category":"type"},{"location":"api/#Grad","page":"API Documentation","title":"Grad","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"Grad\nGrad(::Function, ::Real, ::Int64)","category":"page"},{"location":"api/#KomaMRICore.Grad","page":"API Documentation","title":"KomaMRICore.Grad","text":"grad = Grad(A, T)\ngrad = Grad(A, T, rise)\ngrad = Grad(A, T, rise, delay)\ngrad = Grad(A, T, rise, fall, delay)\n\nThe Gradient struct.\n\nArguments\n\nA: (::Float64, [T]) amplitude of the gradient\nT: (::Float64, [s]) duration of the flat-top\nrise: (::Real, [s]) duration of the rise\nfall: (::Real, [s]) duration of the fall\ndelay: (::Real, [s]) duration of the delay\n\nReturns\n\ngrad: (::Grad) gradient struct\n\n\n\n\n\n","category":"type"},{"location":"api/#KomaMRICore.Grad-Tuple{Function, Real, Int64}","page":"API Documentation","title":"KomaMRICore.Grad","text":"grad = Grad(f::Function, T::Real, N::Int64; delay::Real)\n\nGenerates an arbitrary gradient waveform defined by function f in the interval t ∈ [0,T]. It uses N square gradients uniformly spaced in the interval.\n\nArguments\n\nf: (::Function) function that describes the gradient waveform\nT: (::Real, [s]) duration of the gradient waveform\nN: (::Int64) number of samples of the gradient waveform\n\nKeywords\n\ndelay: (::Real, =0, [s]) starting delay for the waveform\n\nReturns\n\ngrad: (::Grad) gradient struct\n\nExamples\n\njulia> f1 = t -> sin(π*t / 0.8)\n\njulia> seq = Sequence([Grad(f1, 0.8)])\n\njulia> plot_seq(seq)\n\n\n\n\n\n","category":"method"},{"location":"api/#RF","page":"API Documentation","title":"RF","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"RF","category":"page"},{"location":"api/#KomaMRICore.RF","page":"API Documentation","title":"KomaMRICore.RF","text":"rf = RF(A, T)\nrf = RF(A, T, Δf)\nrf = RF(A, T, Δf, delay)\n\nThe RF struct.\n\nArguments\n\nA: (::Complex{Int64}, [T]) the amplitud-phase B1x + i B1y\nT: (::Int64, [s]) the duration of the RF\nΔf: (::Float64, [Hz]) the frequency offset of the RF\ndelay: (::Float64, [s]) the delay time of the RF\n\nReturns\n\nrf: (::RF) the RF struct\n\n\n\n\n\n","category":"type"},{"location":"api/#ADC","page":"API Documentation","title":"ADC","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"ADC","category":"page"},{"location":"api/#KomaMRICore.ADC","page":"API Documentation","title":"KomaMRICore.ADC","text":"adc = ADC(N, T)\nadc = ADC(N, T, delay)\nadc = ADC(N, T, delay, Δf, ϕ)\n\nThe ADC struct.\n\nArguments\n\nN: (::Int64) number of acquired samples\nT: (::Float64, [s]) duration to acquire the samples\ndelay: (::Float64, [s]) delay time to start the acquisition\nΔf: (::Float64, [Hz]) delta frequency. It's meant to compensate RF pulse phases. It is used internally by the read_ADC function\nϕ: (::Float64, [rad]) phase. It's meant to compensate RF pulse phases. It is used internally by the read_ADC function\n\nReturns\n\nadc: (::ADC) ADC struct\n\n\n\n\n\n","category":"type"},{"location":"api/#Delay","page":"API Documentation","title":"Delay","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"Delay","category":"page"},{"location":"api/#KomaMRICore.Delay","page":"API Documentation","title":"KomaMRICore.Delay","text":"delay = Delay(T)\n\nThe Delay struct. It is a special \"object\" meant to add a delay to a sequence by using a sum operator.\n\nArguments\n\nT: (::Real, [s]) time delay value\n\nReturns\n\ndelay: (::Delay) delay struct\n\n\n\n\n\n","category":"type"},{"location":"api/#Read-Data","page":"API Documentation","title":"Read Data","text":"","category":"section"},{"location":"api/#read_seq","page":"API Documentation","title":"read_seq","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"read_seq","category":"page"},{"location":"api/#KomaMRICore.read_seq","page":"API Documentation","title":"KomaMRICore.read_seq","text":"seq = read_seq(filename)\n\nReturns the Sequence struct from a sequence file .seq.\n\nArguments\n\nfilename: (::String) the absolute or relative path of the sequence file .seq\n\nReturns\n\nseq: (::Sequence) Sequence struct\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_seq(seq)\n\n\n\n\n\n","category":"function"},{"location":"api/#read_phantom_jemris","page":"API Documentation","title":"read_phantom_jemris","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"read_phantom_jemris","category":"page"},{"location":"api/#KomaMRICore.read_phantom_jemris","page":"API Documentation","title":"KomaMRICore.read_phantom_jemris","text":"phantom = read_phantom_jemris(filename)\n\nReturns the Phantom struct from a JEMRIS phantom file .h5.\n\nArguments\n\nfilename: (::String) the absolute or relative path of the phantom file .h5\n\nReturns\n\nphantom: (::Phantom) Phantom struct\n\nExamples\n\njulia> obj_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/2.phantoms/brain.h5\")\n\njulia> obj = read_phantom_jemris(obj_file)\n\njulia> plot_phantom_map(obj, :ρ)\n\n\n\n\n\n","category":"function"},{"location":"api/#read_phantom_MRiLab","page":"API Documentation","title":"read_phantom_MRiLab","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"read_phantom_MRiLab","category":"page"},{"location":"api/#KomaMRICore.read_phantom_MRiLab","page":"API Documentation","title":"KomaMRICore.read_phantom_MRiLab","text":"phantom = read_phantom_MRiLab(filename)\n\nReturns the Phantom struct from a MRiLab phantom file .mat.\n\nArguments\n\nfilename: (::String) the absolute or relative path of the phantom file .mat\n\nReturns\n\nphantom: (::Phantom) Phantom struct\n\nExamples\n\njulia> obj_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/2.phantoms/brain.mat\")\n\njulia> obj = read_phantom_MRiLab(obj_file)\n\njulia> plot_phantom_map(obj, :ρ)\n\n\n\n\n\n","category":"function"},{"location":"api/#signal_to_raw_data","page":"API Documentation","title":"signal_to_raw_data","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"signal_to_raw_data","category":"page"},{"location":"api/#KomaMRICore.signal_to_raw_data","page":"API Documentation","title":"KomaMRICore.signal_to_raw_data","text":"raw_ismrmrd = signal_to_raw_data(signal, seq; phantom_name, sys, simParams)\n\nTransforms the raw signal into ISMRMRD format.\n\nArguments\n\nsignal: (::Vector{ComplexF64}) raw signal\nseq: (::Sequence) Sequence struct\n\nKeywords\n\nphantom_name: (::String, =\"Phantom\") Phantom struct\nsys: (::Scanner, =Scanner()) Scanner struct\nsimParams: (::Dict{String,Any}(), =Dict{String,Any}()) dictionary with simulation parameters\n\nReturns\n\nraw_ismrmrd: (::RawAcquisitionData) raw signal in ISMRMRD format\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/3.koma_paper/comparison_accuracy/sequences/EPI/epi_100x100_TE100_FOV230.seq\");\n\njulia> sys, obj, seq = Scanner(), brain_phantom2D(), read_seq(seq_file)\n\njulia> raw = simulate(obj, seq, sys)\n\njulia> plot_signal(raw)\n\n\n\n\n\n","category":"function"},{"location":"api/#Pulse-Design","page":"API Documentation","title":"Pulse Design","text":"","category":"section"},{"location":"api/#PulseDesigner","page":"API Documentation","title":"PulseDesigner","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"PulseDesigner","category":"page"},{"location":"api/#KomaMRICore.PulseDesigner","page":"API Documentation","title":"KomaMRICore.PulseDesigner","text":"PulseDesigner\n\nA module to define different pulse sequences.\n\n\n\n\n\n","category":"module"},{"location":"api/#PulseDesigner.RF_hard","page":"API Documentation","title":"PulseDesigner.RF_hard","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"PulseDesigner.RF_hard","category":"page"},{"location":"api/#KomaMRICore.PulseDesigner.RF_hard","page":"API Documentation","title":"KomaMRICore.PulseDesigner.RF_hard","text":"ex = RF_hard(B1, T, sys::Scanner; G=[0,0,0], Δf=0)\n\nDefinition of the RF hard sequence.\n\nArguments\n\nB1: (Float64, [T]) amplitude of the RF pulse\nT: (Float64, [s]) duration of the RF pulse\nsys: (::Scanner) Scanner struct\n\nKeywords\n\nG: (Vector{Float64}, =[0, 0, 0], [T]) gradient amplitudes for x, y, z\nΔf: (Float64, =0, [Hz]) frequency offset of the RF pulse\n\nReturns\n\nex: (::Sequence) excitation Sequence struct\n\nExamples\n\njulia> sys = Scanner();\n\njulia> durRF = π/2/(2π*γ*sys.B1); #90-degree hard excitation pulse\n\njulia> ex = PulseDesigner.RF_hard(sys.B1, durRF, sys)\nSequence[ τ = 0.587 ms | blocks: 1 | ADC: 0 | GR: 0 | RF: 1 | DEF: 0 ]\n\njulia> plot_seq(ex)\n\n\n\n\n\n","category":"function"},{"location":"api/#PulseDesigner.RF_sinc","page":"API Documentation","title":"PulseDesigner.RF_sinc","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"PulseDesigner.RF_sinc","category":"page"},{"location":"api/#KomaMRICore.PulseDesigner.RF_sinc","page":"API Documentation","title":"KomaMRICore.PulseDesigner.RF_sinc","text":"seq = spiral_base(FOV::Float64, Nr::Int, sys::Scanner)\n\nDefinition of the radial base sequence.\n\nArguments\n\nFOV: (::Float64, [m]) field of view\nN: (::Int) number of pixels along the radious\nsys: (::Scanner) Scanner struct\n\nReturns\n\nex: (::Sequence) RF struct\n\nReferences\n\nMATT A. BERNSTEIN, KEVIN F. KING, XIAOHONG JOE ZHOU, CHAPTER 2 - RADIOFREQUENCY PULSE SHAPES, Handbook of MRI Pulse Sequences, 2004, Pages 35-66, https://doi.org/10.1016/B978-012092861-3/50006-6.\n\n\n\n\n\n","category":"function"},{"location":"api/#PulseDesigner.EPI","page":"API Documentation","title":"PulseDesigner.EPI","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"PulseDesigner.EPI","category":"page"},{"location":"api/#KomaMRICore.PulseDesigner.EPI","page":"API Documentation","title":"KomaMRICore.PulseDesigner.EPI","text":"epi = EPI(FOV::Float64, N::Int, sys::Scanner)\n\nDefinition of the EPI sequence.\n\nArguments\n\nFOV: (::Float64, [m]) field of view\nN: (::Int) number of pixels in the x and y axis\nsys: (::Scanner) Scanner struct\n\nReturns\n\nepi: (::Sequence) epi Sequence struct\n\nExamples\n\njulia> sys, FOV, N = Scanner(), 23e-2, 101\n\njulia> epi = PulseDesigner.EPI(FOV, N, sys)\nSequence[ τ = 62.259 ms | blocks: 203 | ADC: 101 | GR: 205 | RF: 0 | DEF: 4 ]\n\njulia> plot_seq(epi)\n\n\n\n\n\n","category":"function"},{"location":"api/#PulseDesigner.radial_base","page":"API Documentation","title":"PulseDesigner.radial_base","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"PulseDesigner.radial_base","category":"page"},{"location":"api/#KomaMRICore.PulseDesigner.radial_base","page":"API Documentation","title":"KomaMRICore.PulseDesigner.radial_base","text":"seq = radial_base(FOV::Float64, Nr::Int, sys::Scanner)\n\nDefinition of the radial base sequence.\n\nArguments\n\nFOV: (::Float64, [m]) field of view\nN: (::Int) number of pixels along the radious\nsys: (::Scanner) Scanner struct\n\nReturns\n\nseq: (::Sequence) radial base Sequence struct\n\n\n\n\n\n","category":"function"},{"location":"api/#PulseDesigner.spiral_base","page":"API Documentation","title":"PulseDesigner.spiral_base","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"PulseDesigner.spiral_base","category":"page"},{"location":"api/#KomaMRICore.PulseDesigner.spiral_base","page":"API Documentation","title":"KomaMRICore.PulseDesigner.spiral_base","text":"seq = spiral_base(FOV::Float64, Nr::Int, sys::Scanner)\n\nDefinition of the radial base sequence.\n\nArguments\n\nFOV: (::Float64, [m]) field of view\nN: (::Int) number of pixels along the radious\nsys: (::Scanner) Scanner struct\n\nReturns\n\nseq: (::Function) function that returns a Sequence when evaluated\n\nReferences\n\nGlover, G.H. (1999), Simple analytic spiral K-space algorithm. Magn. Reson. Med., 42: 412-415. https://doi.org/10.1002/(SICI)1522-2594(199908)42:2<412::AID-MRM25>3.0.CO;2-U\n\n\n\n\n\n","category":"function"},{"location":"api/#PulseDesigner.EPI_example","page":"API Documentation","title":"PulseDesigner.EPI_example","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"PulseDesigner.EPI_example","category":"page"},{"location":"api/#Simulation","page":"API Documentation","title":"Simulation","text":"","category":"section"},{"location":"api/#simulate","page":"API Documentation","title":"simulate","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"simulate","category":"page"},{"location":"api/#KomaMRICore.simulate","page":"API Documentation","title":"KomaMRICore.simulate","text":"out = simulate(obj::Phantom, seq::Sequence, sys::Scanner; simParams, w)\n\nReturns the raw signal or the last state of the magnetization according to the value of the \"return_type\" key of the simParams dictionary.\n\nArguments\n\nobj: (::Phantom) Phantom struct\nseq: (::Sequence) Sequence struct\nsys: (::Scanner) Scanner struct\n\nKeywords\n\nsimParams: (::Dict{String,Any}, =Dict{String,Any}()) the dictionary with simulation parameters\nw: (::Any, =nothing) the flag to regard a progress bar in the blink window UI. If this variable is differnet from nothing, then the progress bar is considered\n\nReturns\n\nout: (::Vector{ComplexF64} or ::S <: SpinStateRepresentation or RawAcquisitionData) depending if \"return_type\" is \"mat\" or \"state\" or \"raw\" (default) respectively.\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/3.koma_paper/comparison_accuracy/sequences/EPI/epi_100x100_TE100_FOV230.seq\");\n\njulia> sys, obj, seq = Scanner(), brain_phantom2D(), read_seq(seq_file)\n\njulia> raw = simulate(obj, seq, sys)\n\njulia> plot_signal(raw)\n\n\n\n\n\n","category":"function"},{"location":"api/#simulate_slice_profile","page":"API Documentation","title":"simulate_slice_profile","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"simulate_slice_profile","category":"page"},{"location":"api/#KomaMRICore.simulate_slice_profile","page":"API Documentation","title":"KomaMRICore.simulate_slice_profile","text":"M = simulate_slice_profile(seq; z, simParams)\n\nReturns magnetization of spins distributed along z after running the Sequence seq.\n\nArguments\n\nseq: (::Sequence) Sequence struct\n\nKeywords\n\nz: (=range(-2e-2,2e-2,200)) range for the z axis\nsimParams: (::Dict{String, Any}, =Dict{String,Any}(\"Δt_rf\"=>1e-6)) dictionary with simulation parameters\n\nReturns\n\nM: (::Vector{Mag}) final state of the Mag vector\n\n\n\n\n\n","category":"function"},{"location":"api/#Plots","page":"API Documentation","title":"Plots","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"CurrentModule = KomaMRIPlots","category":"page"},{"location":"api/#plot_phantom_map","page":"API Documentation","title":"plot_phantom_map","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_phantom_map","category":"page"},{"location":"api/#KomaMRIPlots.plot_phantom_map","page":"API Documentation","title":"KomaMRIPlots.plot_phantom_map","text":"p = plot_phantom_map(ph, key; t0=0, height=600, width=nothing, darkmode=false)\n\nPlots a phantom map for a specific spin parameter given by key.\n\nArguments\n\nph: (::Phantom) Phantom struct\nkey: (::Symbol, opts: [:ρ, :T1, :T2, :T2s, :x, :y, :z]) symbol for displaying different parameters of the phantom spins\n\nKeywords\n\nt0: (::Float64, =0, [ms]) time to see displacement of the phantom\nheight: (::Int64, =600) height of the plot\nwidth: (::Int64, =nothing) width of the plot\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nview_2d: (::Bool, =false) boolean to use a 2D scatter plot\ncolorbar: (::Bool, =true) boolean to show the colorbar\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the phantom map for a specific spin parameter\n\nReferences\n\nColormaps from https://github.com/markgriswold/MRFColormaps Towards Unified Colormaps for Quantitative MRF Data, Mark Griswold, et al. (2018).\n\nExamples\n\njulia> obj2D, obj3D = brain_phantom2D(), brain_phantom3D();\n\njulia> plot_phantom_map(obj2D, :ρ)\n\njulia> plot_phantom_map(obj3D, :ρ)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_seq","page":"API Documentation","title":"plot_seq","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_seq","category":"page"},{"location":"api/#KomaMRIPlots.plot_seq","page":"API Documentation","title":"KomaMRIPlots.plot_seq","text":"p = plot_seq(seq; width, height, slider, show_seq_blocks, show_sim_blocks, Nblocks,\n darkmode, max_rf_samples, range)\n\nPlots a sequence struct.\n\nArguments\n\nseq: (::Sequence) Sequence struct\n\nKeywords\n\nwidth: (::Int64, =nothing) width of the plot\nheight: (::Int64, =nothing) height of the plot\nslider: (::Bool, =true) boolean to display a slider\nshow_seq_blocks: (::Bool, =false) boolean to show sequence blocks\nshow_sim_blocks: (::Bool, =false) boolean to show simulation blocks\nNblocks: (::Int64, =0) number of simulation blocks to display\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nmax_rf_samples: (::Int64, =100) maximum number of RF samples\nrange: (::Vector{Float64}, =[]) time range to be displayed initially\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the Sequence struct\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_seq(seq)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_kspace","page":"API Documentation","title":"plot_kspace","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_kspace","category":"page"},{"location":"api/#KomaMRIPlots.plot_kspace","page":"API Documentation","title":"KomaMRIPlots.plot_kspace","text":"p = plot_kspace(seq; width=nothing, height=nothing, darkmode=false)\n\nPlots the k-space of a sequence struct.\n\nArguments\n\nseq: (::Sequence) Sequence struct\n\nKeywords\n\nwidth: (::Int64, =nothing) width of the plot\nheight: (::Int64, =nothing) height of the plot\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the k-space of the sequence struct seq\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_kspace(seq)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_M0","page":"API Documentation","title":"plot_M0","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_M0","category":"page"},{"location":"api/#KomaMRIPlots.plot_M0","page":"API Documentation","title":"KomaMRIPlots.plot_M0","text":"p = plot_M0(seq; height=nothing, width=nothing, slider=true, darkmode=false, range=[])\n\nPlots the zero order moment (M0) of a Sequence seq.\n\nArguments\n\nseq: (::Sequence) Sequence struct\n\nKeywords\n\nheight: (::Int64, =nothing) height of the plot\nwidth: (::Int64, =nothing) width of the plot\nslider: (::Bool, =true) boolean to display a slider\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nrange: (::Vector{Float64}, =[]) time range to be displayed initially\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the moment M0 of the sequence struct seq\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_M0(seq)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_M1","page":"API Documentation","title":"plot_M1","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_M1","category":"page"},{"location":"api/#KomaMRIPlots.plot_M1","page":"API Documentation","title":"KomaMRIPlots.plot_M1","text":"p = plot_M1(seq; height=nothing, width=nothing, slider=true, darkmode=false, range=[])\n\nPlots the first order moment (M1) of a Sequence seq.\n\nArguments\n\nseq: (::Sequence) Sequence\n\nKeywords\n\nheight: (::Int64, =nothing) height of the plot\nwidth: (::Int64, =nothing) width of the plot\nslider: (::Bool, =true) boolean to display a slider\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nrange: (::Vector{Float64}, =[]) time range to be displayed initially\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the moment M1 of the sequence struct seq\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_M1(seq)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_M2","page":"API Documentation","title":"plot_M2","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_M2","category":"page"},{"location":"api/#KomaMRIPlots.plot_M2","page":"API Documentation","title":"KomaMRIPlots.plot_M2","text":"p = plot_M2(seq; height=nothing, width=nothing, slider=true, darkmode=false, range=[])\n\nPlots the second order moment (M2) of a Sequence seq.\n\nArguments\n\nseq: (::Sequence) Sequence\n\nKeywords\n\nheight: (::Int64, =nothing) height of the plot\nwidth: (::Int64, =nothing) width of the plot\nslider: (::Bool, =true) boolean to display a slider\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nrange: (::Vector{Float64}, =[]) time range to be displayed initially\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the moment M2 of the sequence struct seq\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_M2(seq)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_eddy_currents","page":"API Documentation","title":"plot_eddy_currents","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_eddy_currents","category":"page"},{"location":"api/#KomaMRIPlots.plot_eddy_currents","page":"API Documentation","title":"KomaMRIPlots.plot_eddy_currents","text":"p = plot_eddy_currents(seq, λ; α=ones(size(λ)), height=nothing, width=nothing, slider=true, darkmode=false, range=[])\n\nPlots the eddy currents of a Sequence seq.\n\nArguments\n\nseq: (::Sequence) Sequence\nλ: (::Float64, [s]) eddy currents decay constant time\n\nKeywords\n\nα: (::Vector{Float64}, =ones(size(λ))) eddy currents factors\nheight: (::Int64, =nothing) height of the plot\nwidth: (::Int64, =nothing) width of the plot\nslider: (::Bool, =true) boolean to display a slider\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nrange: (::Vector{Float64}, =[]) time range to be displayed initially\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the eddy currents of the sequence struct seq\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_eddy_currents(seq, 80e-3)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_slew_rate","page":"API Documentation","title":"plot_slew_rate","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_slew_rate","category":"page"},{"location":"api/#KomaMRIPlots.plot_slew_rate","page":"API Documentation","title":"KomaMRIPlots.plot_slew_rate","text":"p = plot_slew_rate(seq; height=nothing, width=nothing, slider=true, darkmode=false, range=[])\n\nPlots the slew rate currents of a Sequence seq.\n\nArguments\n\nseq: (::Sequence) Sequence\n\nKeywords\n\nheight: (::Int64, =nothing) height of the plot\nwidth: (::Int64, =nothing) width of the plot\nslider: (::Bool, =true) boolean to display a slider\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nrange: (::Vector{Float64}, =[]) time range to be displayed initially\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the slew rate currents of the sequence struct seq\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/1.sequences/spiral.seq\")\n\njulia> seq = read_seq(seq_file)\n\njulia> plot_slew_rate(seq)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_signal","page":"API Documentation","title":"plot_signal","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_signal","category":"page"},{"location":"api/#KomaMRIPlots.plot_signal","page":"API Documentation","title":"KomaMRIPlots.plot_signal","text":"p = plot_signal(raw::RawAcquisitionData; height, width, slider, show_sim_blocks,\n darkmode, range)\n\nPlots a raw signal in ISMRMRD format.\n\nArguments\n\nraw: (::RawAcquisitionData) RawAcquisitionData struct which is the raw signal in ISMRMRD format\n\nKeywords\n\nwidth: (::Int64, =nothing) width of the plot\nheight: (::Int64, =nothing) height of the plot\nslider: (::Bool, =true) boolean to display a slider\nshow_sim_blocks: (::Bool, =false) boolean to show simulation blocks\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\nrange: (::Vector{Float64}, =[]) time range to be displayed initially\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the raw signal\n\nExamples\n\njulia> seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/3.koma_paper/comparison_accuracy/sequences/EPI/epi_100x100_TE100_FOV230.seq\");\n\njulia> sys, obj, seq = Scanner(), brain_phantom2D(), read_seq(seq_file)\n\njulia> raw = simulate(obj, seq, sys)\n\njulia> plot_signal(raw)\n\n\n\n\n\n","category":"function"},{"location":"api/#plot_image","page":"API Documentation","title":"plot_image","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"plot_image","category":"page"},{"location":"api/#KomaMRIPlots.plot_image","page":"API Documentation","title":"KomaMRIPlots.plot_image","text":"p = plot_image(image; height, width, zmin, zmax, darkmode, title)\n\nPlots an image matrix.\n\nArguments\n\nimage: (::Matrix{Float64}) image matrix\n\nKeywords\n\nheight: (::Int64, =750) height of the plot\nwidth: (::Int64, =nothing) width of the plot\nzmin: (::Float64, =minimum(abs.(image[:]))) reference value for minimum color\nzmax: (::Float64, =maximum(abs.(image[:]))) reference value for maximum color\ndarkmode: (::Bool, =false) boolean to define colors for darkmode\ntitle: (::String, =\"\") title of the plot\n\nReturns\n\np: (::PlotlyJS.SyncPlot) plot of the image matrix\n\n\n\n\n\n","category":"function"},{"location":"api/#UI","page":"API Documentation","title":"UI","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"CurrentModule = KomaMRI","category":"page"},{"location":"api/#KomaUI","page":"API Documentation","title":"KomaUI","text":"","category":"section"},{"location":"api/","page":"API Documentation","title":"API Documentation","text":"KomaUI","category":"page"},{"location":"api/#KomaMRI.KomaUI","page":"API Documentation","title":"KomaMRI.KomaUI","text":"out = KomaUI(; kwargs...)\n\nLaunch the Koma's UI.\n\nKeywords\n\ndarkmode: (::Bool, =true) define dark mode style for the UI\nframe: (::Bool, =true) display the upper frame of the Blink window\nphantom_mode: (::String, =\"2D\", opts=[\"2D\", \"3D\"]) load the default phantom as a 2D or 3D brain example\nsim: (::Dict{String,Any}, =Dict{String,Any}()) simulation parameters dictionary\nrec: (::Dict{Symbol,Any}, =Dict{Symbol,Any}()) reconstruction parameters dictionary\nreturn_window: (::Bool, =false) make the out be either 'nothing' or the Blink window, depending on whether the return_window keyword argument is set to true\nshow_window: (::Bool, =true) display the Blink window\n\nReturns\n\nout: (::Nothing or ::Blink.AtomShell.Window) returns either 'nothing' or the Blink window, depending on whether the return_window keyword argument is set to true.\n\nExamples\n\njulia> KomaUI()\n\n\n\n\n\n","category":"function"},{"location":"events/#Sequence-Events","page":"Sequence Events","title":"Sequence Events","text":"","category":"section"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"We refer to RF, Grad, and ADC as \"events\". This section covers the details of how events are defined and manipulated within a Sequence struct.","category":"page"},{"location":"events/#Sequence-Events-2","page":"Sequence Events","title":"Sequence Events","text":"","category":"section"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"As we already know, a Sequence struct contains field names that store arrays of RF, Grad and ADC structs. To create a Sequence, it's essential to understand how to create these fundamental events.","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"In the following subsections, we will provide detailed explanations of event parameters and how to create a Sequence using RF, Grad and ADC events.","category":"page"},{"location":"events/#RF","page":"Sequence Events","title":"RF","text":"","category":"section"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"The RF struct is defined in the source code of KomaMRI as follows:","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"mutable struct RF\n A\n T\n Δf\n delay::Real\nend","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"As you can see, it has 4 field names: ''A'' defines amplitude, ''T'' defines duration time, ''delay'' is the distance between the 0 time and the first waveform sample and ''Δf'' is the displacement respect to the main field carrier frequency (this is for advanced users).","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"''A'' and ''T'' can be numbers or vectors of numbers. Depending on the length of the ''A'' and ''T'', KomaMRI interprets different waveforms: ","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"Pulse Waveform: A and T are numbers\nUniformly-Sampled Waveform: A is a vector and T is a number\nTime-Shaped Waveform: A and T are both vectors with the same length (zero-order-hold)","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"In the image below, we provide a summary of how you can define RF events:","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"warning: Warning\nIn future versions of KomaMRI, the RF interpolation will change to use linear interpolation between two consecutive samples, similar to what is currently done with the Grad struct.","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"Let's look at some basic examples of creating these RF structs and including them in a Sequence struct. The examples should be self-explanatory.","category":"page"},{"location":"events/#RF-Pulse-Waveform","page":"Sequence Events","title":"RF Pulse Waveform","text":"","category":"section"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"julia> A, T, delay = 10e-3, 0.5e-3, 0.1e-3;\n\njulia> rf = RF(A, T, 0, delay)\n←0.1 ms→ RF(10000.0 uT, 0.5 ms, 0.0 Hz)\n\njulia> seq = Sequence(); seq += rf; seq = seq[2:end]\nSequence[ τ = 0.6 ms | blocks: 1 | ADC: 0 | GR: 0 | RF: 1 | DEF: 0 ]\n\njulia> plot_seq(seq; slider=false)","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"","category":"page"},{"location":"events/#RF-Uniformly-Sampled-Waveform","page":"Sequence Events","title":"RF Uniformly-Sampled Waveform","text":"","category":"section"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"julia> tl = -3:0.2:-0.2; tr = 0.2:0.2:3;\n\njulia> A = (10e-3)*[sin.(π*tl)./(π*tl); 1; sin.(π*tr)./(π*tr)];\n\njulia> T, delay = 0.5e-3, 0.1e-3;\n\njulia> rf = RF(A, T, 0, delay)\n←0.1 ms→ RF(∿ uT, 0.5 ms, 0.0 Hz)\n\njulia> seq = Sequence(); seq += rf; seq = seq[2:end]\nSequence[ τ = 0.6 ms | blocks: 1 | ADC: 0 | GR: 0 | RF: 1 | DEF: 0 ]\n\njulia> plot_seq(seq; slider=false)","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"","category":"page"},{"location":"events/#RF-Time-Shaped-Waveform","page":"Sequence Events","title":"RF Time-Shaped Waveform","text":"","category":"section"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"julia> tl = -4:0.2:-0.2; tr = 0.2:0.2:4\n\njulia> A = (10e-3)*[sin.(π*tl)./(π*tl); 1; sin.(π*tr)./(π*tr)]\n\njulia> T = [0.05e-3*ones(length(tl)); 2e-3; 0.05e-3*ones(length(tl))]\n\njulia> delay = 0.1e-3;\n\njulia> rf = RF(A, T, 0, delay)\n←0.1 ms→ RF(∿ uT, 4.0 ms, 0.0 Hz)\n\njulia> seq = Sequence(); seq += rf; seq = seq[2:end]\nSequence[ τ = 4.1 ms | blocks: 1 | ADC: 0 | GR: 0 | RF: 1 | DEF: 0 ]\n\njulia> plot_seq(seq; slider=false)","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"","category":"page"},{"location":"events/#Gradient","page":"Sequence Events","title":"Gradient","text":"","category":"section"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"The Grad struct is defined as follows in the source code of KomaMRI:","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"mutable struct Grad\n A\n T\n rise::Real\n fall::Real\n delay::Real\nend","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"As you can see, it has 5 field names: ''A'' defines amplitude, ''T'' defines duration time, ''delay'' is the distance between the 0 time and the first waveform sample, ''rise'' and ''fall'' are the time durations of the first and last gradient ramps.","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"Just like the RF, ''A'' and ''T'' in the Grad struct can be numbers or vectors of numbers. Depending on the length of the ''A'' and ''T'', KomaMRI interprets different waveforms: ","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"Trapezoidal Waveform: A and T are numbers\nUniformly-Sampled Waveform: A is a vector and T is a number\nTime-Shaped Waveform: A and T are both vectors, A has one sample more the T (linear interpolation)","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"In the image below, we provide a summary of how you can define Grad events:","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"Let's look at some basic examples of creating these Grad structs and including them in a Sequence struct, focusing on the ''x'' component of the gradients. The examples should be self-explanatory.","category":"page"},{"location":"events/#Gradient-Trapezoidal-Waveform","page":"Sequence Events","title":"Gradient Trapezoidal Waveform","text":"","category":"section"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"julia> A, T, delay, rise, fall = 50*10e-6, 5e-3, 2e-3, 1e-3, 1e-3;\n\njulia> gr = Grad(A, T, rise, fall, delay)\n←2.0 ms→ Grad(0.5 mT, 0.5 ms, ↑1.0 ms, ↓1.0 ms)\n\njulia> seq = Sequence([gr])\nSequence[ τ = 9.0 ms | blocks: 1 | ADC: 0 | GR: 1 | RF: 0 | DEF: 0 ]\n\njulia> plot_seq(seq; slider=false)","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"","category":"page"},{"location":"events/#Gradient-Uniformly-Sampled-Waveform","page":"Sequence Events","title":"Gradient Uniformly-Sampled Waveform","text":"","category":"section"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"julia> t = 0:0.25:7.5\n\njulia> A = 10*10e-6 * sqrt.(π*t) .* sin.(π*t)\n\njulia> T = 10e-3;\n\njulia> delay, rise, fall = 1e-3, 0, 1e-3;\n\njulia> gr = Grad(A, T, rise, fall, delay)\n←1.0 ms→ Grad(∿ mT, 10.0 ms, ↑0.0 ms, ↓1.0 ms)\n\njulia> seq = Sequence([gr])\nSequence[ τ = 12.0 ms | blocks: 1 | ADC: 0 | GR: 1 | RF: 0 | DEF: 0 ]\n\njulia> plot_seq(seq; slider=false)","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"","category":"page"},{"location":"events/#Gradient-Time-Shaped-Waveform","page":"Sequence Events","title":"Gradient Time-Shaped Waveform","text":"","category":"section"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"julia> A = 50*10e-6*[1; 1; 0.8; 0.8; 1; 1];\n\njulia> T = 1e-3*[5; 0.2; 5; 0.2; 5];\n\njulia> delay, rise, fall = 1e-3, 1e-3, 1e-3;\n\njulia> gr = Grad(A, T, rise, fall, delay)\n←1.0 ms→ Grad(∿ mT, 15.4 ms, ↑1.0 ms, ↓1.0 ms)\n\njulia> seq = Sequence([gr])\nSequence[ τ = 10.75 ms | blocks: 1 | ADC: 0 | GR: 1 | RF: 0 | DEF: 0 ]\n\njulia> plot_seq(seq; slider=false)","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"","category":"page"},{"location":"events/#ADC","page":"Sequence Events","title":"ADC","text":"","category":"section"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"The ADC struct is defined in the KomaMRI source code as follows:","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"mutable struct ADC\n N::Integer\n T::Real\n delay::Real\n Δf::Real\n ϕ::Real\nend","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"As you can see, it has 5 field names: ''N'' defines number of samples, ''T'' defines total acquisition duration, ''delay'' is the distance between the 0 time and the first sampled signal, ''Δf'' and ''ϕ' are factor to correct signal acquisition (for advanced users).","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"In the image below you can see how to define an ADC event:","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"Let's look at a basic example of defining an ADC struct and including it in a Sequence struct:","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"julia> N, T, delay = 16, 5e-3, 1e-3;\n\njulia> adc = ADC(N, T, delay)\nADC(16, 0.005, 0.001, 0.0, 0.0)\n\njulia> seq = Sequence(); seq += adc; seq = seq[2:end]\nSequence[ τ = 6.0 ms | blocks: 1 | ADC: 1 | GR: 0 | RF: 0 | DEF: 0 ]\n\njulia> plot_seq(seq; slider=false)","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"","category":"page"},{"location":"events/#Combination-of-Events","page":"Sequence Events","title":"Combination of Events","text":"","category":"section"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"We can include multiple events within a single block of a sequence. The example below demonstrates how to combine an RF struct, three Grad structs for the x-y-z components, and an ADC struct in a single block of a sequence:","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"# Define an RF struct\nA, T = 1e-6*[0; -0.1; 0.2; -0.5; 1; -0.5; 0.2; -0.1; 0], 0.5e-3;\nrf = RF(A, T)\n\n# Define a Grad struct for Gx\nA, T, rise = 50*10e-6, 5e-3, 1e-3\ngx = Grad(A, T, rise)\n\n# Define a Grad struct for Gy\nA = 50*10e-6*[0; 0.5; 0.9; 1; 0.9; 0.5; 0; -0.5; -0.9; -1]\nT, rise = 5e-3, 2e-3;\ngy = Grad(A, T, rise)\n\n# Define a Grad struct for Gz\nA = 50*10e-6*[0; 0.5; 0.9; 1; 0.9; 0.5; 0; -0.5; -0.9; -1]\nT = 5e-3*[0.0; 0.1; 0.3; 0.2; 0.1; 0.2; 0.3; 0.2; 0.1]\ngz = Grad(A, T)\n\n# Define an ADC struct\nN, T, delay = 16, 5e-3, 1e-3\nadc = ADC(N, T, delay)","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"julia> seq = Sequence([gx; gy; gz;;], [rf;;], [adc])\nSequence[ τ = 9.0 ms | blocks: 1 | ADC: 1 | GR: 3 | RF: 1 | DEF: 0 ]\n\njulia> plot_seq(seq; slider=false)","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"","category":"page"},{"location":"events/","page":"Sequence Events","title":"Sequence Events","text":"Once the struct events are defined, it's important to note that to create a single block sequence, you need to provide 2D matrices of Grad and RF structs, as well as a vector of ADC structs as arguments in the Sequence constructor.","category":"page"},{"location":"ui-details/#User-Interface","page":"User Interface","title":"User Interface","text":"","category":"section"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"This section explains how to use the user interface of the KomaMRI package and the internal processes during interaction.","category":"page"},{"location":"ui-details/#Basic-Workflow","page":"User Interface","title":"Basic Workflow","text":"","category":"section"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"As a general overview, remember the following workflow steps when using KomaMRI:","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"Loading Simulation Inputs: Scanner, Phantom, Sequence\nRunning Simulation\nReconstructing Image using MRIReco","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"In the following subsections, we will cover all the mentioned steps. First, open the Julia REPL and enter the following commands to include the KomaMRI package and launch the user interface:","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"julia> using KomaMRI\n\njulia> KomaUI()","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"","category":"page"},{"location":"ui-details/#Loading-Simulation-Inputs","page":"User Interface","title":"Loading Simulation Inputs","text":"","category":"section"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"The user interface has preloaded certain inputs into RAM, including the Scanner, Phantom, and Sequence structs. In the following subsections, we will demonstrate how to visualize these inputs.","category":"page"},{"location":"ui-details/#Scanner","page":"User Interface","title":"Scanner","text":"","category":"section"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"You can visualize the preloaded Scanner struct by clicking on the Scanner dropdown and then pressing the View Scanner button. The Scanner struct contains hardware-related information, such as the main magnetic field's magnitude:","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"","category":"page"},{"location":"ui-details/#Phantom","page":"User Interface","title":"Phantom","text":"","category":"section"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"To see the phantom already stored in RAM, simply click on the Phantom dropdown an then press the View Phantom button. The preloaded phantom is a slice of a brain:","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"It is also possible to load .h5 phantom files. The KomaMRI.jl has some examples stored at ~/.julia/packages/KomaMRI//examples/2.phantoms/. For instance, let's load the sphere_chemical_shift.h5 file:","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"Note that you can select different spin parameters to visualize like ρ, T1, T2, among others. ","category":"page"},{"location":"ui-details/#Sequence","page":"User Interface","title":"Sequence","text":"","category":"section"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"There are two options to visualize the sequence already preloaded in RAM: in the time domain or in the k-space. The preloaded sequence is a single-shot EPI.","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"For visualization of the sequence in the time domain, click on the Sequence dropdown and then press the Sequence (MPS) button:","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"For visualization of the sequence in the k-space, click on the Sequence dropdown and then press the k-space button:","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"You can also display the Moments related to the Sequence by pressing the View Moments and then pressing the buttons for zero, first and second moments.","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"It is also possible to load Pulseq compatible .seq sequence files. The KomaMRI has some examples stored at ~/.julia/packages/KomaMRI//examples/1.sequences/. For instance, let's load the spiral.seq file and view it the time domain and k-space:","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"","category":"page"},{"location":"ui-details/#Running-Simulation","page":"User Interface","title":"Running Simulation","text":"","category":"section"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"Once the inputs are loaded in RAM, it is possible to perform the simulation to get the Raw Signal.","category":"page"},{"location":"ui-details/#Simulation-Parameters","page":"User Interface","title":"Simulation Parameters","text":"","category":"section"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"To visualize the default simulation parameters, click on the Simulate! dropdown and then press the View Options button:","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"","category":"page"},{"location":"ui-details/#Visualization-of-the-Raw-Signal","page":"User Interface","title":"Visualization of the Raw Signal","text":"","category":"section"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"Press the Simulate! button to perform the simulation (this may take a while). Automatically the generated Raw Signal should be displayed or you can click on the Raw Data dropdown and then press the View Raw Data button:","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"","category":"page"},{"location":"ui-details/#Reconstructing-Image-using-MRIReco","page":"User Interface","title":"Reconstructing Image using MRIReco","text":"","category":"section"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"Once the Raw Signal is loaded in RAM, it is possible to reconstruct the image.","category":"page"},{"location":"ui-details/#Reconstruction-Parameters","page":"User Interface","title":"Reconstruction Parameters","text":"","category":"section"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"To visualize the default reconstruction parameters, click on the Reconstruct! dropdown and then press the View Options button:","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"","category":"page"},{"location":"ui-details/#Visualization-of-the-Image","page":"User Interface","title":"Visualization of the Image","text":"","category":"section"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"Press the Reconstruct! button to perform the reconstruction (this may take a while). Automatically the generated Image should be displayed or you can click on the he Reconstruct! dropdown and then press the |Image| button:","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"","category":"page"},{"location":"ui-details/#Exporting-Results-to-.mat-File","page":"User Interface","title":"Exporting Results to .mat File","text":"","category":"section"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"(You can also go to analog steps using Scripts)","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"The user interface has the option to save the results in .mat format. Simply click on the Export to .mat and you have the alternatives to get data independently or you can press the All button to have all the results given by the simulator:","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"","category":"page"},{"location":"ui-details/","page":"User Interface","title":"User Interface","text":"So far, and due to limitations of the user interface dependencies, the .mat files are saved in the temporal directory of your computer OS, which can be found by typing the tempdir() command in the Julia REPL.","category":"page"},{"location":"programming-workflow/#Julia-Scripts","page":"Julia Scripts","title":"Julia Scripts","text":"","category":"section"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"You should already be familiar with the Graphical User Interface of KomaMRI. However, you can also use this package directly from the Julia REPL or write your own Julia scripts. This allows you to unlock the full potential of KomaMRI, enabling you to utilize more of its functionalities and even test your own MRI ideas.","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"This section demonstrates a basic workflow with KomaMRI through writing your own scripts or entering commands directly into the Julia REPL. Let's begin.","category":"page"},{"location":"programming-workflow/#Basic-Workflow","page":"Julia Scripts","title":"Basic Workflow","text":"","category":"section"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"As a general overview, remember the following workflow steps when using KomaMRI:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"Loading Simulation Inputs: Scanner, Phantom, Sequence\nRunning Simulation\nReconstructing Image using MRIReco","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"Let's replicate these previous steps in a Julia script. You will obtain the following code, which you can copy and paste into the Julia REPL:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"# Import the package\nusing KomaMRI\n\n# Define scanner, object and sequence\nsys = Scanner()\nobj = brain_phantom2D()\nseq = PulseDesigner.EPI_example()\n\n# Define simulation parameters and perform simulation\nsim_params = KomaMRICore.default_sim_params() \nraw = simulate(obj, seq, sys; sim_params)\n\n# Auxiliary function for reconstruction\nfunction reconstruct_2d_image(raw::RawAcquisitionData)\n acqData = AcquisitionData(raw)\n acqData.traj[1].circular = false #Removing circular window\n C = maximum(2*abs.(acqData.traj[1].nodes[:])) #Normalize k-space to -.5 to .5 for NUFFT\n acqData.traj[1].nodes = acqData.traj[1].nodes[1:2,:] ./ C\n Nx, Ny = raw.params[\"reconSize\"][1:2]\n recParams = Dict{Symbol,Any}()\n recParams[:reconSize] = (Nx, Ny)\n recParams[:densityWeighting] = true\n rec = reconstruction(acqData, recParams)\n image3d = reshape(rec.data, Nx, Ny, :)\n image2d = (abs.(image3d) * prod(size(image3d)[1:2]))[:,:,1]\n return image2d\nend\n\n# Perform reconstruction to get the image\nimage = reconstruct_2d_image(raw)","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"Let's go through this script step by step.","category":"page"},{"location":"programming-workflow/#Loading-Simulation-Inputs","page":"Julia Scripts","title":"Loading Simulation Inputs","text":"","category":"section"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"The inputs of the simulation are created in the following part of the script: ","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"# Define scanner, object and sequence\nsys = Scanner()\nobj = brain_phantom2D()\nseq = PulseDesigner.EPI_example()","category":"page"},{"location":"programming-workflow/#Scanner","page":"Julia Scripts","title":"Scanner","text":"","category":"section"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"The previously created Scanner struct contains default parameters. In your initial simulations, you will likely use this default struct without making any modifications. You can view all the parameters by displaying the struct variable in the Julia REPL. The Scanner's parameters include hardware limitations such as the main magnetic field, maximum gradient values, minimum raster times, and more. You may want to adjust these values for your future custom simulations.","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"julia> sys\nScanner\n B0: Float64 1.5\n B1: Float64 1.0e-5\n Gmax: Float64 0.06\n Smax: Int64 500\n ADC_Δt: Float64 2.0e-6\n seq_Δt: Float64 1.0e-5\n GR_Δt: Float64 1.0e-5\n RF_Δt: Float64 1.0e-6\n RF_ring_down_T: Float64 2.0e-5\n RF_dead_time_T: Float64 0.0001\n ADC_dead_time_T: Float64 1.0e-5","category":"page"},{"location":"programming-workflow/#Phantom","page":"Julia Scripts","title":"Phantom","text":"","category":"section"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"The Phantom struct created in this example represents a slice of a brain. To create it, we use the function brain_phantom2D, which is part of the subdependency KomaMRICore. While KomaMRI provides some phantom examples for experimentation, you may also want to create your custom Phantom struct tailored to your specific requirements.","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"The Phantom struct contains MRI parameters related to the magnetization properties of an object. These parameters include magnetization positions, proton density, relaxation times, off-resonance, among others. To view all the keys and values of the object, you can do so in the Julia REPL as follows:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"julia> obj\nPhantom{Float64}\n name: String \"brain2D_axial\"\n x: Array{Float64}((6506,)) [-0.084, -0.084, … 0.086, 0.086]\n y: Array{Float64}((6506,)) [-0.03, -0.028, … 0.0, 0.002]\n z: Array{Float64}((6506,)) [-0.0, -0.0, … 0.0, 0.0]\n ρ: Array{Float64}((6506,)) [0.7, 0.7, … 0.7, 0.7]\n T1: Array{Float64}((6506,)) [0.569, 0.569, … 0.569, 0.569]\n T2: Array{Float64}((6506,)) [0.329, 0.329, … 0.329, 0.329]\n T2s: Array{Float64}((6506,)) [0.058, 0.058, … 0.058, 0.058]\n Δw: Array{Float64}((6506,)) [-0.0, -0.0, … -0.0, -0.0]\n Dλ1: Array{Float64}((6506,)) [0.0, 0.0, … 0.0, 0.0]\n Dλ2: Array{Float64}((6506,)) [0.0, 0.0, … 0.0, 0.0]\n Dθ: Array{Float64}((6506,)) [0.0, 0.0, … 0.0, 0.0]\n...","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"As you can see, attributes of the Phantom struct are vectors representing object properties, with each element holding a value associated with a single magnetization.","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"You can also visualize the Phantom struct using the plot_phantom_map function, which is part of the KomaMRIPlots subdependency. This function plots the magnitude of a property for each magnetization at a specific spatial position. You can observe properties such as proton density and relaxation times, so feel free to replace the :ρ symbol with another property of the phantom in the example below:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"julia> plot_phantom_map(obj, :ρ)","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"To utilize test phantoms included with KomaMRI, navigate to the \"examples\" folder and use the read_phantom_jemris function to read a phantom in .h5 format. The following steps outline how to do this in Julia:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"julia> path_koma = dirname(dirname(pathof(KomaMRI)))\njulia> path_sphere = joinpath(path_koma, \"examples\", \"2.phantoms\", \"sphere_chemical_shift.h5\")\njulia> sphere = read_phantom_jemris(path_sphere)\njulia> plot_phantom_map(sphere, :T2)","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"","category":"page"},{"location":"programming-workflow/#Sequence","page":"Julia Scripts","title":"Sequence","text":"","category":"section"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"The Sequence struct in the example represents one of the most basic MRI sequences. It excites the object with a 90° RF pulse and then uses EPI gradients to fill the k-space in a \"square\" manner. While you may want to create your sequences for experiments, you can always use some of the examples already available in KomaMRI.","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"In MRI, the sequence must be carefully designed with precise timing to obtain an image. It includes subcomponents such as gradients, radio-frequency excitation signals, and sample acquisition. For more information on constructing a Sequence struct, refer to the Sequence section.","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"You can view general information about a Sequence struct by displaying it in the Julia REPL:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"julia> seq\nSequence[ τ = 62.846 ms | blocks: 204 | ADC: 101 | GR: 205 | RF: 1 | DEF: 5 ]","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"For more precise timing checks, you can use the plot_seq function:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"julia> plot_seq(seq; range=[0 30])","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"It is important to consider how the sequence traverses through k-space. The get_kspace function does precisely that:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"julia> plot_kspace(seq)","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"Additionally, there are helpful sequence construction functions within a submodule of KomaMRI called PulseDesigner. These functions include RF_hard, RF_sinc, EPI, radial_base and spiral_base. For more details on how to use them, refer to the API documentation.","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"KomaMRI is also compatible with Pulseq. The package installation includes some .seq files in Pulseq format, which can be read and used as a Sequence struct. Here's how to read a spiral Pulseq file stored in the \"examples\" folder of KomaMRI:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"julia> path_koma = dirname(dirname(pathof(KomaMRI)))\njulia> path_spiral = joinpath(path_koma, \"examples\", \"1.sequences\", \"spiral.seq\")\njulia> spiral = read_seq(path_spiral)\njulia> plot_seq(spiral)\njulia> plot_kspace(spiral)","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"","category":"page"},{"location":"programming-workflow/#Running-Simulation","page":"Julia Scripts","title":"Running Simulation","text":"","category":"section"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"The following lines in the example script configure and perform the simulation:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"# Define simulation parameters and perform simulation\nsim_params = KomaMRICore.default_sim_params() \nraw = simulate(obj, seq, sys; sim_params)","category":"page"},{"location":"programming-workflow/#Simulation-Parameters","page":"Julia Scripts","title":"Simulation Parameters","text":"","category":"section"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"To perform simulations, KomaMRI requires certain parameters. You can use the default parameters for testing, but you also have the option to customize specific simulation aspects. In the example, we use the default_sim_params function to create a dictionary with default simulation parameters. You can view the keys that can be modified by displaying the sim_params variable:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"julia> sim_params\nDict{String, Any} with 9 entries:\n \"return_type\" => \"raw\"\n \"Nblocks\" => 20\n \"gpu\" => true\n \"Nthreads\" => 1\n \"gpu_device\" => 0\n \"sim_method\" => Bloch()\n \"precision\" => \"f32\"\n \"Δt\" => 0.001\n \"Δt_rf\" => 5.0e-5","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"All of these parameters deserve special attention. We will explain some of the most important ones here. For instance, \"Δt\" and \"Δt_rf\" represent the raster times for the gradients and RFs. \"return_type\" specifies the type of variable returned by the simulator (by default, it returns an object ready for use with MRIReco for reconstruction, but you can use the value \"mat\" to return a simple vector). \"gpu\" indicates whether you want to use your GPU device for simulations, and \"precision\" sets the floating-point precision. For more details on how to set these parameters, please refer to the Simulation Parameters Section.","category":"page"},{"location":"programming-workflow/#Raw-Signal","page":"Julia Scripts","title":"Raw Signal","text":"","category":"section"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"The simulation is performed using the simulate function, which requires three arguments: a Scanner struct, a Phantom struct, and a Sequence struct. Optionally, you can include the keyword argument sim_params if you wish to use custom simulation parameters.","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"In the example, we can see that the output of the simulation is a special struct:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"julia> typeof(raw)\nRawAcquisitionData\n\njulia> raw\nRawAcquisitionData[SeqName: epi | 101 Profile(s) of 101×1]","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"You can plot the simulation result with the plot_signal function like so:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"julia> plot_signal(raw)","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"","category":"page"},{"location":"programming-workflow/#Reconstructing-Image-using-MRIReco","page":"Julia Scripts","title":"Reconstructing Image using MRIReco","text":"","category":"section"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"KomaMRI does not handle reconstruction; instead, you should utilize the MRIReco package to generate an image. For convenience, when you install KomaMRI, you also install MRIReco, allowing you to access functions from that package. You should pay special attention to the RawAcquisitionData and AcquisitionData structs, as well as the reconstruction function.","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"In the example below, we define an auxiliary function, reconstruct_2d_image, which takes a raw signal struct, RawAcquisitionData, as input and returns a 2D Array representing an image. Within this function, we create an AcquisitionData struct and set some reconstruction parameters, which serve as inputs for the reconstruction function. The latter function is responsible for the image generation process.","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"# Auxiliary function for reconstruction\nfunction reconstruct_2d_image(raw::RawAcquisitionData)\n acqData = AcquisitionData(raw)\n acqData.traj[1].circular = false #Removing circular window\n C = maximum(2*abs.(acqData.traj[1].nodes[:])) #Normalize k-space to -.5 to .5 for NUFFT\n acqData.traj[1].nodes = acqData.traj[1].nodes[1:2,:] ./ C\n Nx, Ny = raw.params[\"reconSize\"][1:2]\n recParams = Dict{Symbol,Any}()\n recParams[:reconSize] = (Nx, Ny)\n recParams[:densityWeighting] = true\n rec = reconstruction(acqData, recParams)\n image3d = reshape(rec.data, Nx, Ny, :)\n image2d = (abs.(image3d) * prod(size(image3d)[1:2]))[:,:,1]\n return image2d\nend\n\n# Perform reconstruction to get the image\nimage = reconstruct_2d_image(raw)","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"If you need more information about how to use the AcquisitionData and the how to fill the reconstruction parameters, you need to visit the MRIReco webpage).","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"To display the image, you can use the plot_image function which is part of the KomaMRIPlots subpackage:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"julia> plot_image(image)","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"
","category":"page"},{"location":"programming-workflow/#Exporting-Results-to-.mat-File","page":"Julia Scripts","title":"Exporting Results to .mat File","text":"","category":"section"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"(You can also go to analog steps using UI)","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"Many people in the MRI community uses MATLAB, probably you are one of them and you want to process the raw signal in the MATLAB environment after simulation is done with KomaMRI. Here we show you an example of how to save a .mat file with the information of the raw signal thank to the help of the MAT package:","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"Many people in the MRI community use MATLAB; you might be one of them and may want to process the Raw Signal in the MATLAB environment after simulation is completed with KomaMRI. Here, we provide an example of how to save a .mat file containing the Raw Signal information using the MAT package.","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"# Use the MAT package\nusing MAT\n\n# Perform simulation to return an Array type\nsim_params[\"return_type\"] = \"mat\"\nraw = simulate(obj, seq, sys; sim_params)\n\n# Save the .mat file in the temp directory\nmatwrite(joinpath(tempdir(), \"koma-raw.mat\"), Dict(\"raw\" => raw))","category":"page"},{"location":"programming-workflow/","page":"Julia Scripts","title":"Julia Scripts","text":"Note that we need to simulate to return an array type (not the default RawAcquisitionData), and then we utilize the matwrite function to save a file named \"koma-raw.mat\" in your computer's temporary directory. Now, you can navigate to your temporary directory (which you can find by displaying the result of tempdir() in the Julia REPL) and locate the \"koma-raw.mat\" file.","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"EditURL = \"../../../../examples/literate/examples/03-ChemicalShiftEPI.jl\"","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/#Chemical-Shift-in-an-EPI-sequence","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"","category":"section"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"(Image: ) (Image: )","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"using KomaMRI # hide\nsys = Scanner() # hide","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"For a more realistic example, we will use a brain phantom.","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"obj = brain_phantom2D() # a slice of a brain\np1 = plot_phantom_map(obj, :T2 ; height=400, width=400, view_2d=true)\np2 = plot_phantom_map(obj, :Δw ; height=400, width=400, view_2d=true)\nsavefig(p1, \"../../assets/examples/2-phantom1.html\") # hide\nsavefig(p2, \"../../assets/examples/2-phantom2.html\") # hide","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"At the left, you can see the T_2 map of the phantom, and at the right, the off-resonance Deltaomega. In this example, the fat is the only source of off-resonance (with Delta f = -220mathrmHz) and you can see it in black in the off-resonance map.","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"Then, we will load an EPI sequence, that is well known for being affected by off-resonance. With this sequence, we will be able visualize the effect of the chemical shift.","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"seq_file = joinpath(dirname(pathof(KomaMRI)), \"../examples/3.koma_paper/comparison_accuracy/sequences/EPI/epi_100x100_TE100_FOV230.seq\")\nseq = read_seq(seq_file)\np3 = plot_seq(seq; range=[0 40], slider=true, height=300)\nsavefig(p3, \"../../assets/examples/2-seq.html\") # hide","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"Feel free to explore the sequence's plot 🔍 below!","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"If we simulate this sequence we will end up with the following signal.","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"raw = simulate(obj, seq, sys)\np4 = plot_signal(raw; range=[98.4 103.4] , height=300)\nsavefig(p4, \"../../assets/examples/2-signal.html\") # hide","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"Now, we need to inspect what effect the off-resonance had in the reconstructed image. As you can see, the fat layer is now shifted to a different position 🤯, this is why the effect is called chemical shift!","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"# Get the acquisition data\nacq = AcquisitionData(raw)\nacq.traj[1].circular = false #This is to remove the circular mask\n\n# Setting up the reconstruction parameters\nNx, Ny = raw.params[\"reconSize\"][1:2]\nreconParams = Dict{Symbol,Any}(:reco=>\"direct\", :reconSize=>(Nx, Ny))\nimage = reconstruction(acq, reconParams)\n\n# Plotting the recon\nslice_abs = abs.(image[:, :, 1])\np5 = plot_image(slice_abs; height=400)\nsavefig(p5, \"../../assets/examples/2-recon.html\") # hide","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"
","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"","category":"page"},{"location":"generated/examples/03-ChemicalShiftEPI/","page":"Chemical Shift in an EPI sequence","title":"Chemical Shift in an EPI sequence","text":"This page was generated using Literate.jl.","category":"page"},{"location":"mri-theory/#Simulation","page":"Simulation","title":"Simulation","text":"","category":"section"},{"location":"mri-theory/#General-Overview","page":"Simulation","title":"General Overview","text":"","category":"section"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"KomaMRI simulates the magnetization of each spin of a Phantom for variable magnetic fields given by a Sequence. It is assumed that a single spin is independent of the state of the other spins in the system (a key feature that enables parallelization). Furthermore, there are defined two regimes in the Sequence: excitation and precession. During the latter, the excitation fields are nulled and are useful for simplifying some physical equations.","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"The are more internal considerations in the KomaMRI implementation. The Figure 1 summarizes the functions called to perform the simulation.","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"
\n\n
","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"From the programming perspective, it is needed to call the function simulate with the sim_params dictionary keyword argument. A user can change the values of the following keys:","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"\"return_type\": defines the output of the simulate function. Possible values are \"raw\", \"mat\", and \"state\", corresponding to outputting a MRIReco RawAcquisitionData, the signal values, and the last magnetization state of the simulation, respectively.\n\"sim_method\": defines the type of simulation. The default value is Bloch(), but you can alternatively use the BlochDict() simulation method. Moreover, you have the flexibility to create your own methods without altering the KomaMRI source code; for further details, refer to the Simulation Method Extensibility section.\n\"Δt\": raster time for gradients.\n\"Δt_rf\": raster time for RFs.\n\"precision\": defines the floating-point simulation precision. You can choose between \"f32\" and \"f64\" to use Float32 and Float64 primitive types, respectively. It's important to note that, especially for GPU operations, using \"f32\" is generally much faster.\n\"Nblocks\" divides the simulation into a specified number of time blocks. This parameter is designed to conserve RAM resources, as KomaMRI computes a series of simulations consecutively, each with the specified number of blocks determined by the value of \"Nblocks\".\n\"Nthreads\": divides the Phantom into a specified number of threads. Because spins are modeled independently of each other, KomaMRI can solve simulations in parallel threads, speeding up the execution time.\n\"gpu\": is a boolean that determines whether to use GPU or CPU hardware resources, as long as they are available on the host computer.\n\"gpu_device\": sets the index ID of the available GPU in the host computer.","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"For instance, if you want to perform a simulation on the CPU with float64 precision using the BlochDict() method (assuming you have already defined obj and seq), you can do so like this:","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"# Set non-default simulation parameters and run simulation\nsim_params = KomaMRICore.default_sim_params() \nsim_params[\"gpu\"] = false\nsim_params[\"precision\"] = \"f64\"\nsim_params[\"sim_method\"] = BlochDict()\nraw = simulate(obj, seq, sys; sim_params)","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"Additionally, the user must be aware of the functions run_spin_excitation! and run_spin_precession! which defines the algorithm for excitation and precession regimes respectively and can be changed by the user without modifying the source code (more details at Simulation Method Extensibility).","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"Previous simulation, the Sequence is discretized to consider specific time points which are critical for simulation. The user can control the time between intermediate gradient samples with the parameter Δt. Similarly, the parameter Δt_rf manages the time between RF samples, and can be relatively large for 2D imaging where the slice profile is less relevant.","category":"page"},{"location":"mri-theory/#Computation-Efficiency","page":"Simulation","title":"Computation Efficiency","text":"","category":"section"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"To reduce the memory usage of our simulator, we subdivided time into Nblocks. KomaMRI classifies each block in either the excitation regime or the precession regime before the simulation.","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"We increased the simulation speed by separating the calculations into Nthreads and then performing the GPU parallel operations with CUDA.jl . This separation is possible as all magnetization vectors are independent of one another.","category":"page"},{"location":"mri-theory/#Simulation-Method-Extensibility","page":"Simulation","title":"Simulation Method Extensibility","text":"","category":"section"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"In Julia, functions use different methods based on the input types via multiple dispatch. We used this to specialize the simulation functions for a given sim_method <:SimulationMethod specified in sim_params. For a given simulation method, the function initialize_spin_state outputs a variable Xt <: SpinStateRepresentation that is passed through the simulation (Figure 1). For the default simulation method Bloch, the spin state is of type Mag, but can be extended to a custom representation, like for example EPGs44 or others. Then, the functions run_spin_excitation! and run_spin_precession! can be described externally for custom types sim_method and Xt, extending Koma’s functionalities without the need of modifying the source code and taking advantage of all of Koma’s features.","category":"page"},{"location":"mri-theory/#Bloch-Simulation-Method","page":"Simulation","title":"Bloch Simulation Method","text":"","category":"section"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"This is the default simulation method used by KomaMRI, however it can always be specified by setting the sim_method = Bloch() entry of the sim_params dictionary. In the following subsection, we will explain the physical and mathematical background and some considerations and assumptions that enables to speed up the simulation.","category":"page"},{"location":"mri-theory/#Physical-and-Mathematical-Background","page":"Simulation","title":"Physical and Mathematical Background","text":"","category":"section"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"The Bloch method of KomaMRI simulates the magnetization of each spin by solving the Bloch equations in the rotating frame:","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"beginalign tag1\n\nfracmathrmd boldsymbolMmathrmd t =\n gamma boldsymbolM times boldsymbolB\n- fracM_x hatx + M_y hatyT_2\n- fracM_z hatx + M_0 hatyT_1 \n\nendalign","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"with gamma the gyromagnetic ratio, boldsymbolM = M_x M_y M_z^T the magnetization vector, and","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"boldsymbolB = B_1x(t) B_1y(t) boldsymbolG(t) cdot boldsymbolx + Delta omega(t)^T","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"the effective magnetic field. M_0 is the proton density, T_1 and T_2 are the relaxation times, and Delta omega is the off-resonance, for each position.","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"The Bloch Simulation Method also uses the technique of operator splitting to simplify the solution of Equation (1). This reflects mathematically the intuition of separating the Bloch equations in a rotation operator described by","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"beginalign tag2\n\nfracmathrmdmathrmdt boldsymbolM =\nbeginbmatrix\n 0 gamma B_z -gamma B_y \n-gamma B_z 0 gamma B_x \n gamma B_y -gamma B_x 0\nendbmatrix\nboldsymbolM \n\nendalign","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"and a relaxation operator described by","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"beginalign tag3\n\nfracmathrmdmathrmdt boldsymbolM =\nbeginbmatrix\n-tfrac1T_2 0 0 \n0 -tfrac1T_2 0 \n0 0 -tfrac1T_1\nendbmatrix\nboldsymbolM\n+\nbeginbmatrix\n0 \n0 \ntfracM_0T_1\nendbmatrix \n\nendalign","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"The evolution of the magnetization can then be described as a two-step process for each time step Delta t (Figure 2).","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"
\n\n
","category":"page"},{"location":"mri-theory/#BlochDict-Simulation-Method","page":"Simulation","title":"BlochDict Simulation Method","text":"","category":"section"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"This is another simulation method defined in the source code of KomaMRI. It can be specified by setting the sim_method = BlochDict() entry of the sim_params dictionary.","category":"page"},{"location":"mri-theory/","page":"Simulation","title":"Simulation","text":"note: Note\nThis section is under construction. More explanation of this simulation method is required.","category":"page"},{"location":"#Introduction","page":"Home","title":"Introduction","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"KomaMRI is a Julia package meant to simulate general Magnetic Resonance Imaging (MRI) scenarios. Its name comes from the Japanese word for spinning-top こま (ko-ma) as they precess due to gravity like spins in a magnetic field.","category":"page"},{"location":"","page":"Home","title":"Home","text":"KomaMRI generates raw data by solving the Bloch equations using the specified scanner, phantom and sequence. It also provides a Graphical User Interface (GUI) that encapsulates the whole imaging pipeline (simulation and reconstruction).","category":"page"},{"location":"","page":"Home","title":"Home","text":"\n","category":"page"},{"location":"","page":"Home","title":"Home","text":"KomaMRI can be used in different environments:","category":"page"},{"location":"","page":"Home","title":"Home","text":"User Interface: User-friendly interaction. No Julia programming skills are required. Refer to the User Interface Section to dive into the details of how to use the GUI.\nScripts : Basic knowledge of Julia is required. Refer to the Scripts Section to follow a basic workflow on how to work with KomaMRI.\nNotebooks: Basic knowledge of Julia is required. This serves as an alternative development environment featuring user-friendly interactive tools. For guidance on setting up these environments, refer to the Notebooks Section.","category":"page"},{"location":"","page":"Home","title":"Home","text":"If you are new to KomaMRI, we recommend starting with the \"Getting Started\" section to install Julia, KomaMRI, and perform your first simulation.","category":"page"},{"location":"#Features","page":"Home","title":"Features","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Some of the features of KomaMRI are:","category":"page"},{"location":"","page":"Home","title":"Home","text":"Fast simulations by using CPU and GPU parallelization 🏃💨.\nOpen Source, so anyone can include additional features 🆙.\nCompatibility with community-standards 🤝 like Pulseq .seq and ISMRMRD .mrd.\nCompatibility with Pluto and Jupyter notebooks 🎈\nCross-platform 🌐 thanks to the use of the Julia programming language.\nFriendly user interface for people with no programming skills 😌.\nFlexible API for advanced users 👨💻.","category":"page"},{"location":"#Potential-Use-Cases","page":"Home","title":"Potential Use Cases","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"We see Koma being used in:","category":"page"},{"location":"","page":"Home","title":"Home","text":"The generation of synthetic data to train Machine Learning models.\nTo test novel pulse sequences before implementing them directly in a real scanner (with a Pulseq sequence).\nTeaching exercises for MRI acquisition or reconstruction.","category":"page"}]
}
diff --git a/dev/sequence/index.html b/dev/sequence/index.html
index f5f2513dd..8a8dd26ae 100644
--- a/dev/sequence/index.html
+++ b/dev/sequence/index.html
@@ -1,5 +1,5 @@
-Sequence Definition · KomaMRI.jl: General MRI simulation framework
This section delves into some details about how a sequence is constructed. The sequence definition in KomaMRI is strongly related to the Pulseq definition. After reading this section, you should be able to create your own Sequence structs for conducting custom simulations using the KomaMRI package.
Let's introduce the following simple sequence figure to expand from a visual example to a more general sequence definition:
A sequence can be thought of as an ordered concatenation of blocks over time. Each block is essentially a sequence with a length of 1. Every block consists of an RF pulse, the $(x,y,z)$gradients, and the acquisition of samples. Each block also has an associated time duration. To simplify, we will refer to these components as follows:
\[\begin{matrix*}[l]
+
Sequence · KomaMRI.jl: General MRI simulation framework
This section delves into some details about how a sequence is constructed. The sequence definition in KomaMRI is strongly related to the Pulseq definition. After reading this section, you should be able to create your own Sequence structs for conducting custom simulations using the KomaMRI package.
Let's introduce the following simple sequence figure to expand from a visual example to a more general sequence definition:
A sequence can be thought of as an ordered concatenation of blocks over time. Each block is essentially a sequence with a length of 1. Every block consists of an RF pulse, the $(x,y,z)$gradients, and the acquisition of samples. Each block also has an associated time duration. To simplify, we will refer to these components as follows:
\[\begin{matrix*}[l]
\text{seq[i]} &: & \text{block i of the sequence} \\
\text{seq.RF[i]} &: & \text{RF pulse at the i block} \\
\text{seq.GR.x[i]} &: & \text{gradient x at the i block} \\
@@ -14,7 +14,7 @@
DUR::Array{Any,1}
DEF::Dict{String,Any}
end
As you can see, a Sequence struct contains 5 field names: ''DEF'' contains information for reconstruction steps (so it is not mandatory to fill it), ''DUR'' is a vector that contains the time durations of each block, ''ADC'' is also a vector with the acquisition samples for every block (an vector of ADC structs), ''GR'' is a 2D matrix which 3 rows representing the x-y-z gradients and columns having the samples of each block (a matrix of Grad structs) and ''RF'' is also a 2D matrix where each row represents a different coil and the columns are for different block samples too (a matrix of RF structs). The RF, Grad and ADC are MRI events that will be explained in the section Events Definitions.
Warning
So far, KomaMRI can only manage one coil for RF excitations. However, in future versions, parallel transmit pTX will be managed by adding more ``rows'' to the RF matrix of the Sequence field name.
In order to understand how a Sequence struct can be manipulated in Julia, let's use the EPI sequence example. You can display basic information of the Sequence variable in the Julia REPL:
As you can see, this Sequence has 204 blocks, 1 of these blocks has an RF struct with values different from zero, there are 205 number of Grad structs considering the x-y-z components, 101 ADC structs acquire samples of some blocks and 62.846 ms is the total time duration of the complete Sequence.
To display the sequence in an graph, we can use the plot_seq function:
julia> plot_seq(seq)
This way, you can see exactly where the RF, Grad and ADC structs are located in time.
You can access and filter information for the RF, Grad, ADC, and DUR field names of a Sequence using the dot notation. This allows you to display helpful information about the organization of the Sequence struct:
As you can see, this Sequence has 204 blocks, 1 of these blocks has an RF struct with values different from zero, there are 205 number of Grad structs considering the x-y-z components, 101 ADC structs acquire samples of some blocks and 62.846 ms is the total time duration of the complete Sequence.
To display the sequence in an graph, we can use the plot_seq function:
julia> plot_seq(seq; slider=false)
This way, you can see exactly where the RF, Grad and ADC structs are located in time.
You can access and filter information for the RF, Grad, ADC, and DUR field names of a Sequence using the dot notation. This allows you to display helpful information about the organization of the Sequence struct:
In the following subsections, we will cover all the mentioned steps. First, open the Julia REPL and enter the following commands to include the KomaMRI package and launch the user interface:
julia> using KomaMRI
+User Interface · KomaMRI.jl: General MRI simulation framework
In the following subsections, we will cover all the mentioned steps. First, open the Julia REPL and enter the following commands to include the KomaMRI package and launch the user interface:
The user interface has preloaded certain inputs into RAM, including the Scanner, Phantom, and Sequence structs. In the following subsections, we will demonstrate how to visualize these inputs.
You can visualize the preloaded Scanner struct by clicking on the Scanner dropdown and then pressing the View Scanner button. The Scanner struct contains hardware-related information, such as the main magnetic field's magnitude:
To see the phantom already stored in RAM, simply click on the Phantom dropdown an then press the View Phantom button. The preloaded phantom is a slice of a brain:
It is also possible to load .h5 phantom files. The KomaMRI.jl has some examples stored at ~/.julia/packages/KomaMRI/<id-string>/examples/2.phantoms/. For instance, let's load the sphere_chemical_shift.h5 file:
Note that you can select different spin parameters to visualize like ρ, T1, T2, among others.
There are two options to visualize the sequence already preloaded in RAM: in the time domain or in the k-space. The preloaded sequence is a single-shot EPI.
For visualization of the sequence in the time domain, click on the Sequence dropdown and then press the Sequence (MPS) button:
For visualization of the sequence in the k-space, click on the Sequence dropdown and then press the k-space button:
You can also display the Moments related to the Sequence by pressing the View Moments and then pressing the buttons for zero, first and second moments.
It is also possible to load Pulseq compatible .seq sequence files. The KomaMRI has some examples stored at ~/.julia/packages/KomaMRI/<id-string>/examples/1.sequences/. For instance, let's load the spiral.seq file and view it the time domain and k-space:
Press the Simulate! button to perform the simulation (this may take a while). Automatically the generated Raw Signal should be displayed or you can click on the Raw Data dropdown and then press the View Raw Data button:
Press the Reconstruct! button to perform the reconstruction (this may take a while). Automatically the generated Image should be displayed or you can click on the he Reconstruct! dropdown and then press the |Image| button:
The user interface has the option to save the results in .mat format. Simply click on the Export to .mat and you have the alternatives to get data independently or you can press the All button to have all the results given by the simulator:
So far, and due to limitations of the user interface dependencies, the .mat files are saved in the temporal directory of your computer OS, which can be found by typing the tempdir() command in the Julia REPL.
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
The user interface has preloaded certain inputs into RAM, including the Scanner, Phantom, and Sequence structs. In the following subsections, we will demonstrate how to visualize these inputs.
You can visualize the preloaded Scanner struct by clicking on the Scanner dropdown and then pressing the View Scanner button. The Scanner struct contains hardware-related information, such as the main magnetic field's magnitude:
To see the phantom already stored in RAM, simply click on the Phantom dropdown an then press the View Phantom button. The preloaded phantom is a slice of a brain:
It is also possible to load .h5 phantom files. The KomaMRI.jl has some examples stored at ~/.julia/packages/KomaMRI/<id-string>/examples/2.phantoms/. For instance, let's load the sphere_chemical_shift.h5 file:
Note that you can select different spin parameters to visualize like ρ, T1, T2, among others.
There are two options to visualize the sequence already preloaded in RAM: in the time domain or in the k-space. The preloaded sequence is a single-shot EPI.
For visualization of the sequence in the time domain, click on the Sequence dropdown and then press the Sequence (MPS) button:
For visualization of the sequence in the k-space, click on the Sequence dropdown and then press the k-space button:
You can also display the Moments related to the Sequence by pressing the View Moments and then pressing the buttons for zero, first and second moments.
It is also possible to load Pulseq compatible .seq sequence files. The KomaMRI has some examples stored at ~/.julia/packages/KomaMRI/<id-string>/examples/1.sequences/. For instance, let's load the spiral.seq file and view it the time domain and k-space:
Press the Simulate! button to perform the simulation (this may take a while). Automatically the generated Raw Signal should be displayed or you can click on the Raw Data dropdown and then press the View Raw Data button:
Press the Reconstruct! button to perform the reconstruction (this may take a while). Automatically the generated Image should be displayed or you can click on the he Reconstruct! dropdown and then press the |Image| button:
The user interface has the option to save the results in .mat format. Simply click on the Export to .mat and you have the alternatives to get data independently or you can press the All button to have all the results given by the simulator:
So far, and due to limitations of the user interface dependencies, the .mat files are saved in the temporal directory of your computer OS, which can be found by typing the tempdir() command in the Julia REPL.
Settings
This document was generated with Documenter.jl version 0.27.25 on Thursday 16 November 2023. Using Julia version 1.9.4.
diff --git a/dev/ways-of-using-koma/index.html b/dev/ways-of-using-koma/index.html
new file mode 100644
index 000000000..aae1316ed
--- /dev/null
+++ b/dev/ways-of-using-koma/index.html
@@ -0,0 +1,137 @@
+
+Ways of using Koma · KomaMRI.jl: General MRI simulation framework
KomaMRI can be used in different environments, depending on the degree of flexibility you desire. If you lack prior programming knowledge, we recommend starting with the User Interface. If you seek the full range of flexibility that KomaMRI offers, programming with Scripts is likely your preference. Alternatively, you can utilize the programming environment provided by Notebooks. Detailed explanations for each method of using KomaMRI will be provided in the following sections.
In the following subsections, we will cover all the mentioned steps. First, open the Julia REPL and enter the following commands to include the KomaMRI package and launch the user interface:
The user interface has preloaded certain inputs into RAM, including the Scanner, Phantom, and Sequence structs. In the following subsections, we will demonstrate how to visualize these inputs.
You can visualize the preloaded Scanner struct by clicking on the Scanner dropdown and then pressing the View Scanner button. The Scanner struct contains hardware-related information, such as the main magnetic field's magnitude:
To see the phantom already stored in RAM, simply click on the Phantom dropdown an then press the View Phantom button. The preloaded phantom is a slice of a brain:
It is also possible to load .h5 phantom files. The KomaMRI.jl has some examples stored at ~/.julia/packages/KomaMRI/<id-string>/examples/2.phantoms/. For instance, let's load the sphere_chemical_shift.h5 file:
Note that you can select different spin parameters to visualize like ρ, T1, T2, among others.
There are two options to visualize the sequence already preloaded in RAM: in the time domain or in the k-space. The preloaded sequence is a single-shot EPI.
For visualization of the sequence in the time domain, click on the Sequence dropdown and then press the Sequence (MPS) button:
For visualization of the sequence in the k-space, click on the Sequence dropdown and then press the k-space button:
You can also display the Moments related to the Sequence by pressing the View Moments and then pressing the buttons for zero, first and second moments.
It is also possible to load Pulseq compatible .seq sequence files. The KomaMRI has some examples stored at ~/.julia/packages/KomaMRI/<id-string>/examples/1.sequences/. For instance, let's load the spiral.seq file and view it the time domain and k-space:
Press the Simulate! button to perform the simulation (this may take a while). Automatically the generated Raw Signal should be displayed or you can click on the Raw Data dropdown and then press the View Raw Data button:
Press the Reconstruct! button to perform the reconstruction (this may take a while). Automatically the generated Image should be displayed or you can click on the he Reconstruct! dropdown and then press the |Image| button:
The user interface has the option to save the results in .mat format. Simply click on the Export to .mat and you have the alternatives to get data independently or you can press the All button to have all the results given by the simulator:
So far, and due to limitations of the user interface dependencies, the .mat files are saved in the temporal directory of your computer OS, which can be found by typing the tempdir() command in the Julia REPL.
You should already be familiar with the Graphical User Interface of KomaMRI. However, you can also use this package directly from the Julia REPL or write your own Julia scripts. This allows you to unlock the full potential of KomaMRI, enabling you to utilize more of its functionalities and even test your own MRI ideas.
This section demonstrates a basic workflow with KomaMRI through writing your own scripts or entering commands directly into the Julia REPL. Let's begin.
The previously created Scanner struct contains default parameters. In your initial simulations, you will likely use this default struct without making any modifications. You can view all the parameters by displaying the struct variable in the Julia REPL. The Scanner's parameters include hardware limitations such as the main magnetic field, maximum gradient values, minimum raster times, and more. You may want to adjust these values for your future custom simulations.
The Phantom struct created in this example represents a slice of a brain. To create it, we use the function brain_phantom2D, which is part of the subdependency KomaMRICore. While KomaMRI provides some phantom examples for experimentation, you may also want to create your custom Phantom struct tailored to your specific requirements.
The Phantom struct contains MRI parameters related to the magnetization properties of an object. These parameters include magnetization positions, proton density, relaxation times, off-resonance, among others. To view all the keys and values of the object, you can do so in the Julia REPL as follows:
As you can see, attributes of the Phantom struct are vectors representing object properties, with each element holding a value associated with a single magnetization.
You can also visualize the Phantom struct using the plot_phantom_map function, which is part of the KomaMRIPlots subdependency. This function plots the magnitude of a property for each magnetization at a specific spatial position. You can observe properties such as proton density and relaxation times, so feel free to replace the :ρ symbol with another property of the phantom in the example below:
julia> plot_phantom_map(obj, :ρ)
To utilize test phantoms included with KomaMRI, navigate to the "examples" folder and use the read_phantom_jemris function to read a phantom in .h5 format. The following steps outline how to do this in Julia:
The Sequence struct in the example represents one of the most basic MRI sequences. It excites the object with a 90° RF pulse and then uses EPI gradients to fill the k-space in a "square" manner. While you may want to create your sequences for experiments, you can always use some of the examples already available in KomaMRI.
In MRI, the sequence must be carefully designed with precise timing to obtain an image. It includes subcomponents such as gradients, radio-frequency excitation signals, and sample acquisition. For more information on constructing a Sequence struct, refer to the Sequence section.
You can view general information about a Sequence struct by displaying it in the Julia REPL:
For more precise timing checks, you can use the plot_seq function:
julia> plot_seq(seq; range=[0 30])
It is important to consider how the sequence traverses through k-space. The get_kspace function does precisely that:
julia> plot_kspace(seq)
Additionally, there are helpful sequence construction functions within a submodule of KomaMRI called PulseDesigner. These functions include RF_hard, RF_sinc, EPI, radial_base and spiral_base. For more details on how to use them, refer to the API documentation.
KomaMRI is also compatible with Pulseq. The package installation includes some .seq files in Pulseq format, which can be read and used as a Sequence struct. Here's how to read a spiral Pulseq file stored in the "examples" folder of KomaMRI:
To perform simulations, KomaMRI requires certain parameters. You can use the default parameters for testing, but you also have the option to customize specific simulation aspects. In the example, we use the default_sim_params function to create a dictionary with default simulation parameters. You can view the keys that can be modified by displaying the sim_params variable:
All of these parameters deserve special attention. We will explain some of the most important ones here. For instance, "Δt" and "Δt_rf" represent the raster times for the gradients and RFs. "return_type" specifies the type of variable returned by the simulator (by default, it returns an object ready for use with MRIReco for reconstruction, but you can use the value "mat" to return a simple vector). "gpu" indicates whether you want to use your GPU device for simulations, and "precision" sets the floating-point precision. For more details on how to set these parameters, please refer to the Simulation Parameters Section.
The simulation is performed using the simulate function, which requires three arguments: a Scanner struct, a Phantom struct, and a Sequence struct. Optionally, you can include the keyword argument sim_params if you wish to use custom simulation parameters.
In the example, we can see that the output of the simulation is a special struct:
julia> typeof(raw)
+RawAcquisitionData
+
+julia> raw
+RawAcquisitionData[SeqName: epi | 101 Profile(s) of 101×1]
You can plot the simulation result with the plot_signal function like so:
KomaMRI does not handle reconstruction; instead, you should utilize the MRIReco package to generate an image. For convenience, when you install KomaMRI, you also install MRIReco, allowing you to access functions from that package. You should pay special attention to the RawAcquisitionData and AcquisitionData structs, as well as the reconstruction function.
In the example below, we define an auxiliary function, reconstruct_2d_image, which takes a raw signal struct, RawAcquisitionData, as input and returns a 2D Array representing an image. Within this function, we create an AcquisitionData struct and set some reconstruction parameters, which serve as inputs for the reconstruction function. The latter function is responsible for the image generation process.
# Auxiliary function for reconstruction
+function reconstruct_2d_image(raw::RawAcquisitionData)
+ acqData = AcquisitionData(raw)
+ acqData.traj[1].circular = false #Removing circular window
+ C = maximum(2*abs.(acqData.traj[1].nodes[:])) #Normalize k-space to -.5 to .5 for NUFFT
+ acqData.traj[1].nodes = acqData.traj[1].nodes[1:2,:] ./ C
+ Nx, Ny = raw.params["reconSize"][1:2]
+ recParams = Dict{Symbol,Any}()
+ recParams[:reconSize] = (Nx, Ny)
+ recParams[:densityWeighting] = true
+ rec = reconstruction(acqData, recParams)
+ image3d = reshape(rec.data, Nx, Ny, :)
+ image2d = (abs.(image3d) * prod(size(image3d)[1:2]))[:,:,1]
+ return image2d
+end
+
+# Perform reconstruction to get the image
+image = reconstruct_2d_image(raw)
If you need more information about how to use the AcquisitionData and the how to fill the reconstruction parameters, you need to visit the MRIReco webpage).
To display the image, you can use the plot_image function which is part of the KomaMRIPlots subpackage:
Many people in the MRI community uses MATLAB, probably you are one of them and you want to process the raw signal in the MATLAB environment after simulation is done with KomaMRI. Here we show you an example of how to save a .mat file with the information of the raw signal thank to the help of the MAT package:
Many people in the MRI community use MATLAB; you might be one of them and may want to process the Raw Signal in the MATLAB environment after simulation is completed with KomaMRI. Here, we provide an example of how to save a .mat file containing the Raw Signal information using the MAT package.
# Use the MAT package
+using MAT
+
+# Perform simulation to return an Array type
+sim_params["return_type"] = "mat"
+raw = simulate(obj, seq, sys; sim_params)
+
+# Save the .mat file in the temp directory
+matwrite(joinpath(tempdir(), "koma-raw.mat"), Dict("raw" => raw))
Note that we need to simulate to return an array type (not the default RawAcquisitionData), and then we utilize the matwrite function to save a file named "koma-raw.mat" in your computer's temporary directory. Now, you can navigate to your temporary directory (which you can find by displaying the result of tempdir() in the Julia REPL) and locate the "koma-raw.mat" file.
You can use KomaMRI with popular programming environments such as Pluto and Jupyter notebooks. The following sections show how to set up these notebooks and test KomaMRI with them.
First, install the Pluto module in your Julia environment. Remember to press the ] button to open the Package Manager Session:"
julia>
+
+@(1.9) pkg> add Pluto
Afterward, return to the Julia Session by pressing the backspace button, and then execute the Pluto.run() function:
julia> using Pluto
+
+julia> Pluto.run()
This should automatically open the Pluto dashboard in your default web browser:
Next, create a new notebook by clicking on + Create a new notebook:
Write and run the following code, which is identical to the Free Induction Decay example. Pluto automatically installs the required modules if they are not present on your system. Additionally, note that we do not use KomaMRI directly since we won't be utilizing the KomaUI() function. Instead, we rely on the KomaMRICore and KomaMRIPlots dependencies. To display plots in Pluto, ensure that you import the PlutoPlots package:"
Ensure you have Jupyter installed on your computer. Follow this tutorial for installation using Anaconda.
Next, install the IJulia module in your Julia environment. Remember to press the ] key to open the Package Manager Session:
julia>
+
+(@v1.9) pkg> add IJulia
For this example, make sure to install KomaMRICore and KomaMRIPlots (we do not use KomaMRI directly since we won't be utilizing the KomaUI() function):