Tutorial

This tutorial demonstrates how to implement the GrapeMR.jl package by scripting a setup and directly calling its functions.

  1. Define your physical system: Spins, relaxation values, inhomogeneities, etc.
  2. Optimization parameters: Scheduler parameters and maximum iterations.
  3. Grape Parameters: Time steps, cost function and which fields to optimize.
  4. Generate initial control field: Cubic spline is used by default.
using GrapeMR

Saturation contrast without inhomogeneities

Physical system

Define the initial magnetization state, relaxation times (in seconds), and spin labels. Specify target according to the desired outcome (cost function-dependent) and offset for $B_0$ inhomogeneities in Hertz. Finally, create a Spin object with all spins. Each unique inhomogeneity combination is treated as a separate spin.

M0 = [0.0, 0.0, 1.0]
T1 = [0.6, 0.1]
T2 = [0.3, 0.05]
ΔB1 = [1.0]
B0 = 0.0
offset = collect(-B0:1:B0)
label  = ["s1", "s2"]
target = ["min", "max"]
spins = Spin(M0, T1, T2, offset, ΔB1, target, label)
2-element Vector{Spin}:
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, 0.0, 1.0, "min", "s1", 2.0)
 Spin([0.0, 0.0, 1.0], 0.1, 0.05, 0.0, 1.0, "max", "s2", 2.0)

Parameters

Optimization Parameters

max_iter = get(ENV, "DEV", "false") == "true" ? 1 : 2000  # we set max_iter to 1 if we're in development mode to build the docs faster
poly_start = 0.5
poly_degree = 2
opt_params = OptimizationParams(poly_start, poly_degree, max_iter)
OptimizationParams(0.5, 2, 2000)

Grape Parameters

N = 2000
cost = GrapeMR.saturation_contrast
fields2opt = Dict("B1x" => true, "B1y" => true, "Bz" => false)
grape_params = GrapeParams(N, cost, fields2opt)
GrapeParams{typeof(GrapeMR.saturation_contrast)}(2000, GrapeMR.saturation_contrast, Dict{String, Bool}("B1y" => 1, "B1x" => 1, "Bz" => 0))

Parameter struct

params = Parameters(grape_params, opt_params)
Parameters{typeof(GrapeMR.saturation_contrast)}(GrapeParams{typeof(GrapeMR.saturation_contrast)}(2000, GrapeMR.saturation_contrast, Dict{String, Bool}("B1y" => 1, "B1x" => 1, "Bz" => 0)), OptimizationParams(0.5, 2, 2000))

Initial control field

Generate an initial control field using the spline function. Set the control time control_time and reference amplitude B1ref.

B1ref = 1.0
control_time = 0.5
control_field = spline_RF(N, control_time, B1ref)
ControlField{Float64, Matrix{Float64}, Matrix{Float64}}([0.3841906028583226 0.3848705247668154 … 0.9802906446344607 0.9834930691088447], [0.3841906028583226 0.3848705247668154 … 0.9802906446344607 0.9834930691088447], 1.0, [0.0 0.0 … 0.0 0.0], 0.5)

Run Optimization

Run the optimization with the contructed Spins, configured Parameters and ControlField.

grape_output = grape(params, control_field, spins);

 Final Cost Function Value = 0.018

---------- RF Analysis ----------

Pulse Peak Amplitude = 3.616 [Hz]
Pulse Peak Amplitude = 0.0849 [μT]
Attenuation corresponding to maximum amplitude: 49.3256 [dB]
Maximum power = 1.7088 [W]
Average power = 0.3647 [W]
Pulse energy = 0.1824 [J]

Plot

julia> using GrapeMR, Plots; unicodeplots(); # change the backend so that plots go to stdout and can be rendered in CI/headless mode.
julia> control_fields = plot_control_fields(grape_output.control_field);
julia> display(control_fields) ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Control Fields⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ┌────────────────────────────────────────┐ 2.63474 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⠲⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⡼⠁⠀⠘⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⢰⠃⠀⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⢀⡏⠀⠀⠀⠀⠀⢳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⡼⠀⠀⠀⠀⠀⠀⠈⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠀⠀⠀⢹⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⡞⠀⠀⠀⠀⠀⠀⠀⠀⠘⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ Bx [Hz] │⠀⠀⠀⠀⣸⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⢳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⢰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⣠⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠔⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠲⣄⠀⢀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ -0.115962 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠀⠀⠉⠑⠒⠒⠋⠙⠒⠒⠒⠒⠒⠒⠦⠀│ └────────────────────────────────────────┘ ⠀-0.015⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀t [s]⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀0.515⠀ ┌────────────────────────────────────────┐ 2.63474 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⠲⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⡼⠁⠀⠘⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⢰⠃⠀⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⢀⡏⠀⠀⠀⠀⠀⢳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⡼⠀⠀⠀⠀⠀⠀⠈⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠀⠀⠀⢹⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⡞⠀⠀⠀⠀⠀⠀⠀⠀⠘⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ By [Hz] │⠀⠀⠀⠀⣸⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⢳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⢰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⣠⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠔⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢳⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠲⣄⠀⢀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ -0.115962 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠀⠀⠉⠑⠒⠒⠋⠙⠒⠒⠒⠒⠒⠒⠦⠀│ └────────────────────────────────────────┘ ⠀-0.015⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀t [s]⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀0.515⠀

Magnetization saturation with $B_0$ inhomogeneity

Physical system

Optimizing pulses robust against field inhomogeneities. Here $B_0$ is the field inhomogeneity in Hertz and $ΔB1$ corresponds to the RF field inhomogeneity in percentage.

M0 = [0.0, 0.0, 1.0]
T1 = [0.6]
T2 = [0.3]
ΔB1 = [1.0]
B0 = 15.0
label  = ["s1"]
target = ["saturation"]
offset = -B0:1:B0
spins = Spin(M0, T1, T2, offset, ΔB1, target, label)
31-element Vector{Spin}:
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, -15.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, -14.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, -13.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, -12.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, -11.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, -10.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, -9.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, -8.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, -7.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, -6.0, 1.0, "saturation", "s1", 31.0)
 ⋮
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, 7.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, 8.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, 9.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, 10.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, 11.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, 12.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, 13.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, 14.0, 1.0, "saturation", "s1", 31.0)
 Spin([0.0, 0.0, 1.0], 0.6, 0.3, 15.0, 1.0, "saturation", "s1", 31.0)

Parameters

Optimization parameters

max_iter = get(ENV, "DEV", "false") == "true" ? 1 : 2000  # we set max_iter to 1 if we're in development mode to build the docs faster
poly_start = 0.5
poly_degree = 2
opt_params = OptimizationParams(poly_start, poly_degree, max_iter)
OptimizationParams(0.5, 2, 2000)

Grape parameters

N = 2000
cost = GrapeMR.euclidean_norm
fields2opt = Dict("B1x" => true, "B1y" => true, "Bz" => false)
grape_params = GrapeParams(N, cost, fields2opt)
GrapeParams{typeof(GrapeMR.euclidean_norm)}(2000, GrapeMR.euclidean_norm, Dict{String, Bool}("B1y" => 1, "B1x" => 1, "Bz" => 0))

Parameter struct

params = Parameters(grape_params, opt_params)
Parameters{typeof(GrapeMR.euclidean_norm)}(GrapeParams{typeof(GrapeMR.euclidean_norm)}(2000, GrapeMR.euclidean_norm, Dict{String, Bool}("B1y" => 1, "B1x" => 1, "Bz" => 0)), OptimizationParams(0.5, 2, 2000))

Initial control fields

B1ref = 5.0
control_time = 0.5
control_field = spline_RF(N, control_time, B1ref)
ControlField{Float64, Matrix{Float64}, Matrix{Float64}}([1.498843192874832 1.4983158796565927 … 1.9099266851981733 1.9052170246939395], [1.498843192874832 1.4983158796565927 … 1.9099266851981733 1.9052170246939395], 5.0, [0.0 0.0 … 0.0 0.0], 0.5)

Run optimization

grape_output = grape(params, control_field, spins);

 Final Cost Function Value = 0.164

---------- RF Analysis ----------

Pulse Peak Amplitude = 19.8301 [Hz]
Pulse Peak Amplitude = 0.4657 [μT]
Attenuation corresponding to maximum amplitude: 15.2843 [dB]
Maximum power = 86.0508 [W]
Average power = 8.9393 [W]
Pulse energy = 4.4696 [J]

Plots

julia> using GrapeMR, Plots; unicodeplots(); # change the backend so that plots go to stdout and can be rendered in CI/headless mode.
julia> control_fields = plot_control_fields(grape_output.control_field);
julia> display(control_fields) ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Control Fields⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ┌────────────────────────────────────────┐ 14.5949 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡏⠘⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⢰⢦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⡎⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⣇⠀⠀⠀⠀⠀⠀⠀⠀⣸⠙⡆⠀⠀⠀⡼⠹⡄⠀⠀⠀│ Bx [Hz] │⠀⢀⡇⠈⡇⠀⠀⢰⣆⠀⠀⠀⢀⡀⠀⠀⢸⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⢀⡇⠀⢳⠀⠀⢠⠇⠀⢣⠀⠀⠀│ │⠀⢸⠀⠀⡇⠀⠀⡏⢸⠀⠀⠀⡜⢳⠀⠀⢸⠀⠀⢸⠀⠀⠀⢰⠛⢦⠀⣸⠀⠀⠸⡄⠀⡼⠀⠀⠘⡆⠀⠀│ │⠀⡏⠀⠀⢹⠀⢀⠇⠘⡆⠀⠀⡇⢸⠀⠀⢸⠀⠀⢸⡀⠀⠀⡏⠀⠈⠳⠃⠀⠀⠀⢧⢠⠇⠀⠀⠀⠘⠶⠀│ │⠀⠀⠀⠀⢸⠀⢸⠀⠀⡇⠀⢰⠁⠈⡇⠀⡼⠀⠀⠀⡇⠀⢸⠁⠀⠀⠀⠀⠀⠀⠀⠈⠉⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠈⡇⡞⠀⠀⢳⠀⢸⠀⠀⡇⠀⡇⠀⠀⠀⡇⠀⡞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠳⠃⠀⠀⢸⠀⡏⠀⠀⢳⠀⡇⠀⠀⠀⢹⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣧⠇⠀⠀⢸⢰⠃⠀⠀⠀⠈⠛⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ -5.64779 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠀⠀⠀⠈⠿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ └────────────────────────────────────────┘ ⠀-0.015⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀t [s]⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀0.515⠀ ┌────────────────────────────────────────┐ 14.5949 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡏⠘⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⢰⢦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⡎⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⣇⠀⠀⠀⠀⠀⠀⠀⠀⣸⠙⡆⠀⠀⠀⡼⠹⡄⠀⠀⠀│ By [Hz] │⠀⢀⡇⠈⡇⠀⠀⢰⣆⠀⠀⠀⢀⡀⠀⠀⢸⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⢀⡇⠀⢳⠀⠀⢠⠇⠀⢣⠀⠀⠀│ │⠀⢸⠀⠀⡇⠀⠀⡏⢸⠀⠀⠀⡜⢳⠀⠀⢸⠀⠀⢸⠀⠀⠀⢰⠛⢦⠀⣸⠀⠀⠸⡄⠀⡼⠀⠀⠘⡆⠀⠀│ │⠀⡏⠀⠀⢹⠀⢀⠇⠘⡆⠀⠀⡇⢸⠀⠀⢸⠀⠀⢸⡀⠀⠀⡏⠀⠈⠳⠃⠀⠀⠀⢧⢠⠇⠀⠀⠀⠘⠶⠀│ │⠀⠀⠀⠀⢸⠀⢸⠀⠀⡇⠀⢰⠁⠈⡇⠀⡼⠀⠀⠀⡇⠀⢸⠁⠀⠀⠀⠀⠀⠀⠀⠈⠉⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠈⡇⡞⠀⠀⢳⠀⢸⠀⠀⡇⠀⡇⠀⠀⠀⡇⠀⡞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠳⠃⠀⠀⢸⠀⡏⠀⠀⢳⠀⡇⠀⠀⠀⢹⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣧⠇⠀⠀⢸⢰⠃⠀⠀⠀⠈⠛⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ -5.64779 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠀⠀⠀⠈⠿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ └────────────────────────────────────────┘ ⠀-0.015⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀t [s]⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀0.515⠀

This page was generated using Literate.jl.