`BlochSimulators.EPGStates`

— Type`EPGStates = Union{MMatrix{3}, SizedMatrix{3}}`

In the EPG model, the configuration state matrix `Ω`

will be updated inplace. On CPU, we use `StaticArrays.MMatrix`

. The MMatrix will not escape `_simulate!`

and therefore should not result in allocations. On GPU, we use shared memory (`CUDA.CuStaticSharedArray`

). The shared memory is allocated for all threads within a block simultaneously. We then take a `@view`

and wrap it in a `SizedArray`

. Without the `SizedArray`

, the type would be long and unreadable. By wrapping, we can then simply dispatch on a `SizedArray`

instead.

`BlochSimulators.AbstractTissueParameters`

— Type`AbstractTissueParameters{N,T} <: FieldVector{N,T}`

Abstract type for structs that hold different combinations of tissue parameters.

**Possible fields**

`T₁::T`

: T₁ relaxation parameters of a voxel`T₂::T`

: T₂ relaxation parameters of a voxel`B₁::T`

: Scaling factor for effective B₁ excitation field within a voxel`B₀::T`

: Off-resonance with respect to main magnetic field within a voxel`ρˣ::T`

: Real part of proton density within a voxel`ρʸ::T`

: Imaginary part of proton density within a voxel`x::T`

: Position of voxel along the x direction`y::T`

: Position of voxel along the y direction`z::T`

: Position of voxel along the z direction

The structs are subtypes of FieldVector, which is a StaticVector with named fields (see the documentation of StaticArrays.jl). There are three reasons for letting the structs be subtypes of FieldVector:

- FieldVectors/StaticVectors have sizes that are known at compile time. This is beneficial for performance reasons
- The named fields improve readability of the code (e.g.
`p.B₁`

vs`p[3]`

) - Linear algebra operations can be performed on instances of the structs. This allows, for example, subtraction (without having to manually define methods) and that is useful for comparing parameter maps.

`BlochSimulators.AbstractTrajectory`

— Type`AbstractTrajectory`

The abstract type of which all gradient trajectories will be a subtype. The subtypes should contain fields that can describe the full trajectory during a sequence.

`BlochSimulators.AdiabaticInversion`

— Type`AdiabaticInversion{T<:Real, V<:AbstractVector} <: IsochromatSimulator{T}`

This struct is used to simulate an adiabatic inversion pulse. This struct itself could be used as field in other sequence structs.

**Fields**

`γΔtA::V`

: # Time-dependent amplitude modulation.`Δω::V`

# Time-dependent frequency modulation`Δt::T`

: Time discretization step, assumed constant

`BlochSimulators.BlochSimulator`

— Type`BlochSimulator{T}`

The abstract type of which all sequence simulators will be a subtype. The parameter `T`

should be a number type (e.g. `Float64`

, `Float32`

) and the tissueparameters that are used as input to the simulator should have the same number type. By convention, a BlochSimulator will be used to simulate magnetization at echo times only without taking into account spatial encoding gradients (i.e. readout or phase encoding gradients). To simulate the magnetization at other readout times, including phase from spatial encoding gradients, an `AbstractTrajectory`

will be needed as well.

To make a simulator for a particular pulse sequence:

Make a struct that's a subtype of either

`IsochromatSimulator`

or`EPGSimulator`

. The struct will hold parameters that are necessary for performing the simulations.Add a method to

`simulate_magnetization!`

that implements the pulse sequence. For both performance and GPU compatibility, make sure that`simulate_magnetization!`

does not do any heap allocations. Examples for`pSSFP`

and`FISP`

sequences are found in`src/sequences`

.Add methods to

`output_eltype`

and`output_size`

that are used to allocate an output array within the simulate function.[Optional] Add a method to show for nicer printing of the sequence in the REPL

[Optional] Add a method to getindex to easily reduce the length of the sequence

[Optional] Add a constructor for the struct that takes in data from Matlab or something else and assembles the struct

**IMPORTANT**

The `simulate_magnetization!`

functions (which dispatch on the provided sequence) are assumed to be type-stable and non-allocating Should be possible to achieve when using functions from `operators/epg.jl`

`and`

operators/isochromat.jl` and a properly parametrized sequence struct.

`BlochSimulators.CartesianTrajectory`

— Type`CartesianTrajectory{T,I,U,V} <: SpokesTrajectory`

Struct that is used to implement a typical Cartesian gradient trajectory. The trajectory is described in a compact fashion by only storing the starting position in k-space (`k_start_readout`

) for each readout as well as the step in k-space per readout point `Δk_adc`

.

Note that CartesianTrajectory and RadialTrajectory are essentially the same in when using when using this compact description. A SpokesTrajectory struct is therefore defined as a supertype of both and methods are defined for SpokesTrajectory instead to avoid code repetition.

The type parameters are intentionally left vague. The `J`

, for example, may be an integer for sequences where each readout has the same number of samples, but for sequences with different numbers of samples per readout it may be a vector of integers.

**Fields**

`nreadouts::I`

: The total number of readouts for this trajectory`nsamplesperreadout::I`

: The total number of samples per readout`Δt::T`

: Time between sample points`k_start_readout::U`

: Starting position in k-space for each readout`Δk_adc::U`

: k-space step Δkₓ per sample point (same for all readouts)`py::V`

: Phase encoding index for each readout

`BlochSimulators.EPGSimulator`

— Type`EPGSimulator{T,Ns} <: BlochSimulator{T}`

Abstract type of which all sequence simulators that are based on the EPG model will be a subtype. The parameter `T`

should be a number type (e.g. `Float64`

, `Float32`

) and the tissueparameters that are used as input to the simulator should have the same number type. The parameter `Ns`

corresponds to the maximum order of configuration states that are tracked in the simulations.

`BlochSimulators.FISP2D`

— Type`FISP2D{T, Ns, U<:AbstractVector, V<:AbstractMatrix} <: EPGSimulator{T,Ns}`

This struct is used to simulate gradient-spoiled sequence with varying flip angle scheme and adiabatic inversion prepulse using the EPG model. The TR and TE are fixed throughout the sequence. Instantenous RF excitations are assumed. Slice profile correction is done using the partitioned EPG model, where for each flip angle a vector of RF scaling factors are determined prior to the simulation (using, for example, the small tip-angle approximation or Shinnar LeRoux forward model).

Within each TR, a single time steps is used to simulate the RF excitation. Then, in one time step we go from the end of the RF excitation to the echo time (applying T₁ and T₂ decay, T₁ regrowth and B₀ rotation), and again in one time step from the echo time to the start of the next RF excitation.

**Fields**

`RF_train::U`

Vector with flip angle for each TR with abs.(RF*train) the RF flip angles in degrees and angle.(RF*train) should be the RF phases in degrees.`sliceprofiles::V`

# Matrix with RF scaling factors (a.u.) to simulate slice profile effects. Each column represents the (flip angle dependent) scaling factors for one position along the slice direction.`TR::T`

: Repetition time in seconds, assumed constant during the sequence`TE::T`

: Echo time in seconds, assumed constant during the sequence`max_state::Val{Ns}`

: Maximum number of states to keep track of in EPG simulation`TI::T`

: Inversion delay after the inversion prepulse in seconds

`BlochSimulators.Generic2D`

— Type`Generic2D{T,V,M,S} where {T<:AbstractFloat, V<:AbstractVector, M<:AbstractMatrix, S} <: IsochromatSimulator{T}`

Simulate a generic 2D sequence defined by arrays containing RF and gradient waveforms. Contains a loop over z locations to take into account slice profile effects. The Δt vector stores the time intervals for the waveforms.

**Fields**

`RF::V{Complex{T}}`

: Vector with (complex) RF values during each time interval`GR::M{T}`

: Matrix with GRx, GRy and GRz values during each time interval`sample::S`

: Vector with Bool's to indicate the sample points`Δt::V{T}`

: Vector with time intervals`z::V{T}`

: Vector with different positions along the slice direction

`BlochSimulators.Generic3D`

— Type`Generic3D{T,V<:AbstractVector{Complex{T}},W<:AbstractVector{T},M<:AbstractMatrix{T},S} <: IsochromatSimulator{T}`

Simulate a generic sequence defined by arrays containing RF and gradient waveforms. Unlike the Generic2D sequence, it is assumed that the excitation is homogenous over the voxel and therefore no summation over a slice direction is applied. The Δt vector stores the time intervals for the waveforms.

**Fields**

`RF::V{Complex{T}}`

: Vector with (complex) RF values during each time interval`GR::M{T}`

: Matrix with GRx, GRy and GRz values during each time interval`sample::S`

: Vector with Bool's to indicate the sample points`Δt::V{T}`

: Vector with time intervals

`BlochSimulators.Isochromat`

— Type```
struct Isochromat{T<:Real} <: FieldVector{3,T}
x::T
y::T
z::T
end
```

Holds the x,y,z components of a spin isochromat in a FieldVector, which is a `StaticVector`

(from the package `StaticArrays`

) with custom fieldnames.

`BlochSimulators.IsochromatSimulator`

— Type`IsochromatSimulator{T} <: BlochSimulator{T}`

Abstract type of which all sequence simulators that are based on the isochromat model will be a subtype. The parameter `T`

should be a number type (e.g. `Float64`

, `Float32`

) and the tissueparameters that are used as input to the simulator should have the same number type.

`BlochSimulators.RadialTrajectory`

— Type`RadialTrajectory{T,I,U,V} <: SpokesTrajectory`

Struct that is used to implement a typical radial gradient trajectory. The trajectory can is described in a compact fashion by only storing the starting position in k-space (`k_start_readout`

) for each readout as well as the step in k-space per readout point `Δk_adc`

.

Note that CartesianTrajectory and RadialTrajectory are essentially the same in when using when using this compact description. A SpokesTrajectory struct is therefore defined as a supertype of both and methods are defined for SpokesTrajectory instead to avoid code repetition.

The type parameters are intentionally left vague. The `J`

, for example, may be an integer for sequences where each readout has the same number of samples, but for sequences with different numbers of samples per readout it may be a vector of integers.

**Fields**

`nreadouts::I`

: The total number of readouts for this trajectory`nsamplesperreadout::I`

: The total number of samples per readout`Δt::T`

: Time between sample points`k_start_readout::U`

: Starting position in k-space for each readout`Δk_adc::U`

: k-space step Δk between each readout`φ::V`

: Radial angle for each readout

`BlochSimulators.SpokesTrajectory`

— Type`SpokesTrajectory <: AbstractTrajectory`

Typical Cartesian and radial trajectories have a lot in common: a readout can be described by a starting point in k-space and a Δk per sample point. To avoid code repetition, both type of trajectories are made a subtype of SpokesTrajectory such that some methods that would be the same for both trajectories otherwise are written for SpokesTrajectory instead.

`BlochSimulators.T₁T₂`

— Type`T₁T₂{T} <: AbstractTissueParameters{2,T}`

`BlochSimulators.T₁T₂B₀`

— Type`T₁T₂B₀{T} <: AbstractTissueParameters{2,T}`

`BlochSimulators.T₁T₂B₀xy`

— Type`T₁T₂B₀xy{T} <: AbstractTissueParameters{5,T}`

`BlochSimulators.T₁T₂B₀xyz`

— Type`T₁T₂B₀xyz{T} <: AbstractTissueParameters{6,T}`

`BlochSimulators.T₁T₂B₀ρˣρʸ`

— Type`T₁T₂B₀ρˣρʸ{T} <: AbstractTissueParameters{5,T}`

`BlochSimulators.T₁T₂B₀ρˣρʸxy`

— Type`T₁T₂B₀ρˣρʸxy{T} <: AbstractTissueParameters{7,T}`

`BlochSimulators.T₁T₂B₀ρˣρʸxyz`

— Type`T₁T₂B₀ρˣρʸxyz{T} <: AbstractTissueParameters{8,T}`

`BlochSimulators.T₁T₂B₁`

— Type`T₁T₂B₁{T} <: AbstractTissueParameters{3,T}`

`BlochSimulators.T₁T₂B₁B₀`

— Type`T₁T₂B₁B₀{T} <: AbstractTissueParameters{4,T}`

`BlochSimulators.T₁T₂B₁B₀xy`

— Type`T₁T₂B₁B₀xy{T} <: AbstractTissueParameters{6,T}`

`BlochSimulators.T₁T₂B₁B₀xyz`

— Type`T₁T₂B₁B₀xyz{T} <: AbstractTissueParameters{7,T}`

`BlochSimulators.T₁T₂B₁B₀ρˣρʸ`

— Type`T₁T₂B₁B₀ρˣρʸ{T} <: AbstractTissueParameters{6,T}`

`BlochSimulators.T₁T₂B₁B₀ρˣρʸxy`

— Type`T₁T₂B₁B₀ρˣρʸxy{T} <: AbstractTissueParameters{8,T}`

`BlochSimulators.T₁T₂B₁B₀ρˣρʸxyz`

— Type`T₁T₂B₁B₀ρˣρʸxyz{T} <: AbstractTissueParameters{9,T}`

`BlochSimulators.T₁T₂B₁xy`

— Type`T₁T₂B₁xy{T} <: AbstractTissueParameters{5,T}`

`BlochSimulators.T₁T₂B₁xyz`

— Type`T₁T₂B₁xyz{T} <: AbstractTissueParameters{6,T}`

`BlochSimulators.T₁T₂B₁ρˣρʸ`

— Type`T₁T₂B₁ρˣρʸ{T} <: AbstractTissueParameters{5,T}`

`BlochSimulators.T₁T₂B₁ρˣρʸxy`

— Type`T₁T₂B₁ρˣρʸxy{T} <: AbstractTissueParameters{7,T}`

`BlochSimulators.T₁T₂B₁ρˣρʸxyz`

— Type`T₁T₂B₁ρˣρʸxyz{T} <: AbstractTissueParameters{8,T}`

`BlochSimulators.T₁T₂xy`

— Type`T₁T₂xy{T} <: AbstractTissueParameters{4,T}`

`BlochSimulators.T₁T₂xyz`

— Type`T₁T₂xyz{T} <: AbstractTissueParameters{5,T}`

`BlochSimulators.T₁T₂ρˣρʸ`

— Type`T₁T₂ρˣρʸ{T} <: AbstractTissueParameters{4,T}`

`BlochSimulators.T₁T₂ρˣρʸxy`

— Type`T₁T₂ρˣρʸxy{T} <: AbstractTissueParameters{6,T}`

`BlochSimulators.T₁T₂ρˣρʸxyz`

— Type`T₁T₂ρˣρʸxyz{T} <: AbstractTissueParameters{7,T}`

`BlochSimulators.pSSFP2D`

— Type`pSSFP2D{T<:AbstractFloat,N,M,U<:AbstractVector{Complex{T}},V<:Number} <: IsochromatSimulator{T}`

This struct is used to simulate a inversion-recovery, gradient-balanced transient-state sequence with varying flip angle scheme based on the isochromat model. The TR and TE are fixed throughout the sequence. The TR and TE are fixed throughout the sequence. Slice profile correction is done by discretizing the RF excitation waveform in time and using multiple `Isochromat`

s with different positions along the slice direction (`z`

) per voxel. The sequence also uses an 'α/2' prepulse after the inversion.

Within each TR, multiple time steps are used to simulate the RF excitation. Then, in one time step we go from the end of the RF excitation to the echo time (applying slice refocussing gradient, T₂ decay and B₀ rotation), and again in one time step from the echo time to the start of the next RF excitation.

**Fields**

`RF_train::U`

Vector with flip angle for each TR with abs.(RF*train) the RF flip angles in degrees and angle.(RF*train) should be the RF phases in degrees.`TR::T`

: Repetition time in seconds, assumed constant during the sequence`γΔtRF::SVector{N}{V}`

: Time-discretized RF waveform, normalized to flip angle of 1 degree`Δt::NamedTuple{(:ex, :inv, :pr),NTuple{3,T}}`

: Time interval for each sample of excitation pulse (ex), inversion delay (inv) and time between RF and TE (pr)`γΔtGRz::NamedTuple{(:ex, :inv, :pr),NTuple{3,T}}`

: Slice select gradients for ex, inv and pr`z::SVector{M}{T}`

# Vector with different positions along the slice direction.

`BlochSimulators.pSSFP3D`

— Type`pSSFP3D{T<:AbstractFloat,N,U<:AbstractVector{Complex{T}},V<:Number} <: IsochromatSimulator{T}`

This struct is used to simulate an inversion-recovery, gradient-balanced, transient-state sequence with varying flip angle scheme based on the isochromat model. The TR and TE are fixed throughout the sequence. The RF excitation waveform can be discretized in time but no slice profile mechanism is provided. The sequence also uses an 'α/2' prepulse after the inversion.

Within each TR, multiple time steps are used to simulate the RF excitation. Then, in one time step we go from the end of the RF excitation to the echo time (applying slice refocussing gradient, T₂ decay and B₀ rotation), and again in one time step from the echo time to the start of the next RF excitation.

**Fields**

`RF_train::U`

Vector with flip angle for each TR with abs.(RF*train) the RF flip angles in degrees and angle.(RF*train) should be the RF phases in degrees.`TR::T`

: Repetition time in seconds, assumed constant during the sequence`γΔtRF::SVector{N}{V}`

: Time-discretized RF waveform, normalized to flip angle of 1 degree`Δt::NamedTuple{(:ex, :inv, :pr),NTuple{3,T}}`

: Time interval for each sample of excitation pulse (ex), inversion delay (inv) and time between RF and TE (pr)

`BlochSimulators.F̄₋`

— Method`F̄₋(Ω)`

View into the second row of the configuration state matrix `Ω`

, corresponding to the `F̄₋`

states.

`BlochSimulators.F₊`

— Method`F₊(Ω)`

View into the first row of the configuration state matrix `Ω`

, corresponding to the `F₊`

states.

`BlochSimulators.Z`

— Method`Z(Ω)`

View into the third row of the configuration state matrix `Ω`

, corresponding to the `Z`

states.

`BlochSimulators._allocate_output`

— Method`_allocate_output(resource, sequence::BlochSimulator, parameters)`

Allocate an array to store the output of the Bloch simulations (per voxel, echo times only) to be performed with the `sequence`

. For each `BlochSimulator`

, methods should have been added to `output_eltype`

and `output_dimensions`

for this function to work properly.

`BlochSimulators._allocate_signal_output`

— Method`_allocate_signal_output(resource, trajectory::AbstractTrajectory, coil_sensitivities)`

Allocate an array to store the output of the signal simulation (all readout points, integrated over all voxels).

`BlochSimulators._get_readout_and_sample_idx`

— Method`_get_readout_and_sample_idx(trajectory, t)`

Given time index `t`

, compute the associated readout and sample indices `(r,s)`

. For trajectories where each readout has the same length, this is equivalent to `r,s = fld1(t,ns), mod1(t,ns)`

with ns being the (constant) number of samples per readout.

`BlochSimulators.add_gradient_delay!`

— Method`add_gradient_delay!(tr::RadialTrajectory, S)`

Apply gradient delay to radial trajectory in in-place fashion. The delay is described by the 2x2 matrix S and is assumed to influence the start of the readout only, not the readout direction.

`BlochSimulators.decay!`

— Method`decay!(Ω::EPGStates, E₁, E₂)`

T₂ decay for F-components, T₁ decay for `Z`

-component of each state.

`BlochSimulators.decay`

— Method`decay(m::Isochromat{T}, E₁, E₂) where T`

Apply T₂ decay to transverse component and T₁ decay to longitudinal component of `Isochromat`

.

`BlochSimulators.dephasing!`

— Method`dephasing!(Ω::EPGStates)`

Shift states around due to dephasing gradient: The `F₊`

go up one, the `F̄₋`

go down one and `Z`

do not change

`BlochSimulators.excite!`

— Method`excite!(Ω::EPGStates, RF::Complex, p::AbstractTissueParameters)`

Mixing of states due to RF pulse. Magnitude of RF is the flip angle in degrees. Phase of RF is the phase of the pulse. If RF is real, the computations simplify a little bit.

`BlochSimulators.excite!`

— Method`excite!(Ω::EPGStates, RF::T, p::AbstractTissueParameters) where T<:Union{Real, Quantity{<:Real}}`

If RF is real, the calculations simplify (and probably Ω is real too, reducing memory (access) requirements).

`BlochSimulators.f32`

— Method`f32(x)`

Change precision of `x`

to `Float32`

. It uses `Functors.fmap`

to recursively traverse the fields of the struct `x`

. For custom structs (e.g. `<:BlochSimulator`

or `<:AbstractTrajectory`

), it is required that `typeof(x)`

be made a `Functors.@functor`

(e.g. `@functor FISP`

).

It may be necessary to add new adapt rules (by adding new methods to adapt_storage) if new structs with complicated nested fields are introduced.

`BlochSimulators.f64`

— Method`f64(x)`

Change precision of `x`

to `Float64`

. It uses `Functors.fmap`

to recursively traverse the fields of the struct `x`

. For custom structs (e.g. `<:BlochSimulator`

or `<:AbstractTrajectory`

), it is required that `typeof(x)`

be made a `Functors.@functor`

(e.g. `@functor FISP`

).

It may be necessary to add new adapt rules (by adding new methods to `adapt_storage`

) if new structs with complicated nested fields are introduced.

`BlochSimulators.gpu`

— Method`gpu(x)`

Move `x`

to CUDA device. It uses `Functors.fmap`

to recursively traverse the fields of the struct `x`

, converting `<:AbstractArrays`

to `CuArrays`

, and ignoring isbitsarrays. For custom structs (e.g. `<:BlochSimulator`

or `<:AbstractTrajectory`

), it is required that `typeof(x)`

be made a `Functors.@functor`

(e.g. `@functor FISP`

).

`BlochSimulators.initial_conditions!`

— Method`initial_conditions!(Ω::EPGStates)`

Set all components of all states to 0, except the Z-component of the 0th state which is set to 1.

`BlochSimulators.initialize_states`

— Method`initialize_states(::AbstractResource, sequence::EPGSimulator{T,Ns}) where {T,Ns}`

Initialize an `MMatrix`

of EPG states on CPU to be used throughout the simulation.

`BlochSimulators.initialize_states`

— Method`initialize_states(::CUDALibs, sequence::EPGSimulator{T,Ns}) where {T,Ns}`

Initialize an array of EPG states on a CUDA GPU to be used throughout the simulation.

`BlochSimulators.initialize_states`

— Method`initialize_states(::AbstractResource, ::IsochromatSimulator{T}) where T`

Initialize a spin isochromat to be used throughout a simulation of the sequence.

This may seem redundant but to is necessary to share the same programming interface with `EPGSimulators`

.

`BlochSimulators.invert!`

— Method`invert!(Ω::EPGStates, p::AbstractTissueParameters)`

Invert `Z`

-component of states of all orders. *Assumes fully spoiled transverse magnetization*.

`BlochSimulators.invert!`

— Method`invert!(Ω::EPGStates)`

Invert with B₁ insenstive (i.e. adiabatic) inversion pulse

`BlochSimulators.invert`

— Method`invert(m::Isochromat{T}, p::AbstractTissueParameters) where T`

Invert `Isochromat`

with B₁ insenstive (i.e. adiabatic) inversion pulse

`BlochSimulators.invert`

— Method`invert(m::Isochromat{T}, p::AbstractTissueParameters) where T`

Invert z-component of `Isochromat`

(assuming spoiled transverse magnetization so xy-component zero).

`BlochSimulators.kspace_coordinates`

— Method`kspace_coordinates(tr::CartesianTrajectory)`

Return matrix (nrsamplesperreadout, nrreadouts) with kspace coordinates for the trajectory. Needed for nuFFT reconstructions.

`BlochSimulators.kspace_coordinates`

— Method`kspace_coordinates(tr::RadialTrajectory)`

Return matrix (nrsamplesperreadout, nrreadouts) with kspace coordinates for the trajectory. Needed for nuFFT reconstructions.

`BlochSimulators.magnetization_to_signal`

— Method`magnetization_to_signal(resource, magnetizationtization, parameters, trajectory, coil_sensitivities)`

Given the magnetization in all voxels (typically at echo times only), allocate memory for the signal output on CPU, then loop over all time points `t`

and use the (generic) `magnetization_to_signal!`

implementation to compute the signal for that time point.

This loop order is not necessarily optimal (and performance may be) across all trajectories and computational resources. If a better implementation is available, add new methods to this function for those specific combinations of resources and trajectories.

`BlochSimulators.nreadouts`

— Method`nreadouts(::AbstractTrajectory)`

For each `::AbstractTrajectory`

, a method should be added to this function that specifies how many readouts the trajectory consists of.

`BlochSimulators.nsamples`

— Method`nsamples(trajectory::AbstractTrajectory)`

Determines the total number of samples acquired with the trajectory. Requires `nreadouts`

and `nsamplesperreadout`

to be implemented.

`BlochSimulators.nsamplesperreadout`

— Method`nsamplesperreadout(::AbstractTrajectory, readout_idx)`

For each `::AbstractTrajectory`

, a method should be added to this function that specifies how many samples in total are acquired during the trajectory.

`BlochSimulators.output_dimensions`

— Method`output_dimensions(::BlochSimulator)`

For each `<:BlochSimulator`

, a method should be added to this function that specifies the output size of the simulation for a *single* `::AbstractTissueParameters`

.

`BlochSimulators.output_eltype`

— Method`output_eltype(::BlochSimulator)`

For each `<:BlochSimulator`

, a method should be added to this function that specifies the output type of the simulation. For MR signal simulation, this is typically a complex number representing the transverse magnetization. For other types of simulations, one may want to retrieve the x,y and z components of an isochromat as output (implemented as a `FieldVector`

perhaps) or state configuration matrices `Ω`

.

`BlochSimulators.phase_encoding!`

— Method`phase_encoding!(magnetization, trajectory::AbstractTrajectory, parameters)`

For each `::AbstractTrajectory`

, a method should be added to this function if it does any kind of phase encoding (so far Cartesian only).

`BlochSimulators.regrowth!`

— Method`regrowth!(Ω::EPGStates, E₁)`

T₁ regrowth for Z-component of 0th order state.

`BlochSimulators.regrowth`

— Method`regrowth(m::Isochromat{T}, E₁) where T`

Apply T₁ regrowth to longitudinal component of `Isochromat`

.

`BlochSimulators.rotate!`

— Method`rotate!(Ω::EPGStates, eⁱᶿ::T) where T`

Rotate `F₊`

and `F̄₋`

states under the influence of `eⁱᶿ = exp(i * ΔB₀ * Δt)`

`BlochSimulators.rotate`

— Method`rotate(m::Isochromat, γΔtGRz, z, Δt, p::AbstractTissueParameters)`

Rotation of Isochromat without RF (so around z-axis only) due to gradients and B0 (i.e. refocussing slice select gradient).

`BlochSimulators.rotate`

— Method`rotate(m::Isochromat{T}, γΔtRF::Complex, γΔtGR::Tuple, (x,y,z), Δt, p::AbstractTissueParameters, Δω = zero(T)) where T`

RF, gradient and/or ΔB₀ induced rotation of Isochromat computed using Rodrigues rotation formula (https://en.wikipedia.org/wiki/Rodrigues%27*rotation*formula).

`BlochSimulators.rotate_decay!`

— Method`rotate_decay!(Ω::EPGStates, E₁, E₂, eⁱᶿ)`

Rotate and decay combined

`BlochSimulators.sample_transverse!`

— Method`sample!(output, index::Union{Integer,CartesianIndex}, m::Isochromat)`

Sample transverse magnetization from `Isochromat`

. The "+=" is needed for 2D sequences where slice profile is taken into account.

`BlochSimulators.sample_transverse!`

— Method`sample_transverse!(output, index::Union{Integer,CartesianIndex}, Ω::EPGStates)`

Sample the measurable transverse magnetization, that is, the `F₊`

component of the 0th state. The `+=`

is needed for 2D sequences where slice profile is taken into account.

`BlochSimulators.sample_xyz!`

— Method`sample_xyz!(output, index::Union{Integer,CartesianIndex}, m::Isochromat)`

Sample m.x, m.y and m.z components from `Isochromat`

. The "+=" is needed for 2D sequences where slice profile is taken into account.

`BlochSimulators.sample_Ω!`

— Method`sample_Ω!(output, index::Union{Integer,CartesianIndex}, Ω::EPGStates)`

Sample the entire configuration state matrix `Ω`

. The `+=`

is needed for 2D sequences where slice profile is taken into account.

`BlochSimulators.sampling_mask`

— Method`sampling_mask(tr::CartesianTrajectory)`

For undersampled Cartesian trajectories, the gradient trajectory can also be described by a sampling mask.

`BlochSimulators.simulate_magnetization!`

— Method`simulate_magnetization!(magnetization, sequence::BlochSimulator, state, p::AbstractTissueParameters) end`

For each `<:BlochSimulator`

, a method should be added to this function that implements the actual pulse sequence using information contained in the sequence struct together with the operators from `src/operators/{isochromat,epg}.jl`

. For performance reasons as well as GPU compatibility it is important that the implementation is type-stable and non-allocating.

**Arguments**

`magnetization`

: Pre-allocated array with`size(magnetization) = output_dimensions(sequence)`

and`eltype(magnetization) = output_eltype(sequence)`

to store the output of the simulation.`sequence`

: Sequence struct containing fields that are used to implement the actual pulse sequence.`state`

: Either an`Isochromat`

or`EPGStates`

, depending on which model is used.`p`

: Custom struct (`<:AbstractTissueParameters`

) containing input parameters to the simulation (e.g.`T₁T₂`

)

`BlochSimulators.simulate_magnetization`

— Method`simulate_magnetization(resource, sequence, parameters)`

Simulate the magnetization at echo times (without any spatial encoding gradients applied) for all combinations of tissue parameters contained in `parameters`

.

This function can also be used to generate dictionaries for MR Fingerprinting purposes.

**Arguments**

`resource::AbstractResource`

: Either`CPU1()`

,`CPUThreads()`

,`CPUProcesses()`

or`CUDALibs()`

`sequence::BlochSimulator`

: Custom sequence struct`parameters::AbstractVector{<:AbstractTissueParameters}`

: Vector with different combinations of tissue parameters

**Returns**

`output::AbstractArray`

: Array of size (output_dimensions(sequence), length(parameters)) containing the magnetization at echo times for all combinations of input tissue parameters.

`BlochSimulators.simulate_magnetization`

— Method`simulate_magnetization(::CPU1, sequence, parameters)`

Perform simulations on a single CPU by looping over all entries of `parameters`

and performing Bloch simulations for each combination of tissue parameters.

`BlochSimulators.simulate_magnetization`

— Method`simulate_magnetization(::CPUProcesses, sequence, dparameters::DArray)`

Perform simulations using multiple, distributed CPUs. See the Julia documentation and the DistributedArrays package for more details on how to use Julia with multiple workers.

`BlochSimulators.simulate_magnetization`

— Method`simulate_magnetization(::CPUThreads, sequence, parameters)`

Perform simulations by looping over all entries of `parameters`

in a multi-threaded fashion. See the Julia documentation for more details on how to launch Julia with multiple threads of execution.

`BlochSimulators.simulate_magnetization`

— Method`simulate_magnetization(::CUDALibs, sequence, parameters::CuArray)`

Perform simulations on NVIDIA GPU hardware by making use of the CUDA.jl package. Each thread perform Bloch simulations for a single entry of the `parameters`

array.

`BlochSimulators.simulate_signal`

— Method`simulate_signal(resource, sequence, parameters, trajectory, coil_sensitivities)`

Simulate the MR signal at timepoint `t`

from coil `i`

as: `sᵢ(t) = ∑ⱼ cᵢⱼρⱼmⱼ(t)`

, where `cᵢⱼ`

is the coil sensitivity of coil `i`

at position of voxel `j`

, `ρⱼ`

is the proton density of voxel `j`

and `mⱼ(t)`

the (normalized) transverse magnetization in voxel `j`

obtained through Bloch simulations.

**Arguments**

`resource::AbstractResource`

: Either`CPU1()`

,`CPUThreads()`

,`CPUProcesses()`

or`CUDALibs()`

`sequence::BlochSimulator`

: Custom sequence struct`parameters::AbstractVector{<:AbstractTissueParameters}`

: Vector with tissue parameters for each voxel`trajectory::AbstractTrajectory`

: Custom trajectory struct`coil_sensitivities::AbstractVector{<:SVector{ncoils}}`

: Vector with`ncoils`

coil sensitivities for each voxel

**Returns**

`signal::Vector{<:SVector{ncoils}}`

: Simulated MR signal for the`sequence`

and`trajectory`

.

At each timepoint, the signal for each of the `ncoils`

is stored.

`BlochSimulators.spoil!`

— Method`spoil!(Ω::EPGStates)`

Perfectly spoil the transverse components of all states.

`BlochSimulators.to_sample_point`

— Method`to_sample_point(m, trajectory, readout_idx, sample_idx, parameters)`

For each ::AbstractTrajectory, a method should be added to this function that, given the magnetization `m`

at the readout with index `readout_idx`

, it computes the magnetization at the readout point with index `sample_idx`

(by applying spatial encoding gradients, T₂ decay, B₀ rotation, etc...) based on the `trajectory`

and `parameters`

.

Arguments

`m`

: Magnetization at the echo with index`readout_idx`

.`trajectory`

: Trajectory struct containing fields that are used to compute the magnetization at other readout times, including the effects of spatial encoding gradients.`readout_idx`

: Index that corresponds to the current readout.`sample_idx`

: Index for the desired sample during this readout.`parameters`

: Tissue parameters of current voxel, including spatial coordinates.

Output:

- mₛ: Magnetization at sample with index
`sample_idx`

`BlochSimulators.Ω_eltype`

— Method`Ω_eltype(sequence::EPGSimulator{T,Ns}) where {T,Ns} = Complex{T}`

By default, configuration states are complex. For some sequences, they will only ever be real (no RF phase, no complex slice profile correction) and for these sequences a method needs to be added to this function.