API Reference
Gradients
These functions deal with gradient pulse sequences.
SpinDoctor.GeneralGradient — TypeGeneralGradient(g⃗, TE)General gradient sequence $\vec{g}(t) \in \mathbb{R}^3$. The direction and amplitude may vary in time.
SpinDoctor.ScalarGradient — TypeScalarGradient(dir, profile, amplitude)Gradient sequence with amplitude amplitude, normal direction dir and scalar time profile profile.
The direction is constant, while the amplitude is controlled by the time profile.
The direction is normalized upon construction.
SpinDoctor.PGSE — Typef = PGSE(δ, Δ)Pulsed Gradient Spin Echo sequence with pulse duration δ and time between pulses Δ-δ.
SpinDoctor.DoublePGSE — Typef = DoublePGSE(δ, Δ)Double Pulsed Gradient Spin Echo sequence with four pulses of duration δ, separated by pauses of duration Δ-δ, 0, and Δ-δ repsectively.
SpinDoctor.CosOGSE — Typef = CosOGSE(δ, Δ, nperiod)Oscillating Gradient Spin Echo sequence with two cos-pulses of duration δ separated by a pause of duration Δ-δ for nperiod periods per pulse.
SpinDoctor.SinOGSE — Typef = SinOGSE(δ, Δ, nperiod)Oscillating Gradient Spin Echo sequence with two sin-pulses of duration δ separated by a pause of duration Δ-δ for nperiod periods per pulse.
SpinDoctor.integral — Functionintegral(f, t = echotime(f))Integral of time profile f between 0 and t. Unless specified, the echotime is used as the upper integral limit.
For the PGSE, SinOGSE, CosOGSE and DoublePGSE sequences, analytical expressions are available. Otherwise a numerical integral is computed.
SpinDoctor.int_F² — Functionint_F²(f)Compute the time profile contribution to the $b$-value given the time profile f. The $b$-value is given by
b = γ^2 * g^2 * int_F²(f)where γ is the gyromagnetic ratio of the water proton and g is the gradient amplitude.
SpinDoctor.intervals — Functionintervals(f)Get characteristic intervals of the time profile f.
SpinDoctor.isconstant — Functionisconstant(profile)Return true if the time profile profile is intervalwise constant.
SpinDoctor.echotime — Functionechotime(f::TimeProfile)Get echo time $TE$ of the time profile f, which is the end of the last characteristic interval.
echotime(grad::AbstractGradient)Get echo time $TE$ of gradient.
Geometry
SpinDoctor.assemble_matrices — Functionassemble_matrices(model)Assemble finite element matrices.
SpinDoctor.split_mesh — Functionsplit_mesh(mesh_all)Split global mesh into compartments.
SpinDoctor.split_field — Functionsplit_field(mesh, ξ)Split the field ξ into a vector containing a view of each compartment.
SpinDoctor.get_cmpt_volumes — Functionget_cmpt_volumes(mesh)Get compartment volumes.
SpinDoctor.Model — TypeModel(; mesh, ρ, D, T₂, κ, γ)Finite element discretized biological model with initial spin densities ρ, diffusion tensors D, T₂-relaxation times T₂, wall permeabilities κ, and gyromacnetic ratio γ. The vectors ρ, D, T₂, are of length ncompartment, while κ is of length nboundary.
SpinDoctor.initial_conditions — Functioninitial_conditions(model)Get initial conditions at all degrees of freedom.
Postprocessing
SpinDoctor.savefield — Functionsavefield(mesh, ξ, filename::String; fieldname = "Magnetization")Save field ξ to a VTK file. It may then be visualized in Paraview.
SpinDoctor.compute_adc_sta — Functioncompute_adc_sta(model)Compute the ADC for each compartment in the short diffusion time regime.
SpinDoctor.fit_adc — Functionfit_adc(bvalues, signals)Fit the apparent diffusion coefficient (ADC) using a polynomial logfit of the normalized signals signals against the b-values bvalues.
SpinDoctor.fit_tensors — Functionfit_tensors(directions, adcs)Fit effective diffusion tensors to directionalized ADCs. The six components of the symmetric diffusion tensors are fitted by least squares to the gradient directions and resulting ADCs.
SpinDoctor.plot_mesh — Functionplot_mesh(femesh, compartments = 1:ncompartment)Plot finite element mesh, with a subset of the compartments.
SpinDoctor.plot_field — Functionplot_field(femesh, ξ)Plot field ξ on the finite element mesh.
Problems
SpinDoctor allows for considering a wide range of diffusion MRI problems. These can be solved for using the solve function.
SpinDoctor.BTPDE — TypeGeneralBTPDE(; model, matrices)Bloch-Torrey PDE problem.
SpinDoctor.HADC — TypeHADC(; model, matrices)HADC problem.
SpinDoctor.Karger — TypeKarger(; model, difftensors)Karger problem.
SpinDoctor.Laplace — TypeLaplace(; model, matrices, neig_max)Laplace eigenvalue problem.
SpinDoctor.MatrixFormalism — TypeMatrixFormalism(; model, matrices, lap_eig)Matrix formalism problem. Given a Laplace eigendecomposition lap_eig, this problem consists of computing the MF magnetization.
SpinDoctor.AnalyticalLaplace — TypeAnalyticalLaplace(; ρ, r, D, W, T₂, γ, dim, eiglim, eigstep)Analytical radial Laplace eigenvalue problem.
SpinDoctor.AnalyticalMatrixFormalism — TypeAnalyticalMatrixFormalism(; analytical_laplace, lap_mat, volumes)Analytical Matrix formalism problem. Given a radial Laplace eigendecomposition analytical_laplace, this problem consists of computing the MF compartment signals.
SpinDoctor.IntervalConstantSolver — TypeIntervalConstantSolver(; θ = 0.5, timestep)BTPDE solver specialized on intervalwise constant ScalarGradients, e.g PGSE, DoublePGSE. Raises an error for other gradients.
SpinDoctor.solve — Functionsolve(problem::Laplace)Compute the Laplace eigenvalues, eigenfunctions and first order moments of products of pairs of eigenfunctions.
solve(problem::MatrixFormalism, gradient; ninterval = 500)Solve for magnetization using Matrix Formalism.
solve(problem::AnalyticalLaplace)Solve analytical Laplace eigenvalue problem for symmetrical geometries.
solve(problem::AnalyticalMatrixFormalism, gradient)Compute the signal in a multilayered cylinder or sphere using an analytical matrix formalism solution.
This function is based on the following articles and corresponding code: [1] D. S. Grebenkov, NMR Survey of Reflected Brownian Motion, Rev. Mod.Phys. 79, 1077 (2007) [2] D. S. Grebenkov, Pulsed-gradient spin-echo monitoring of restricted diffusion in multilayered structures, J. Magn. Reson. 205, 181-195 (2010).
solve(
problem::GeneralBTPDE,
gradient,
odesolver = QNDF();
abstol = 1e-6,
reltol = 1e-4,
callbacks = [].
)Solve the Bloch-Torrey partial differential equation using P1 finite elements in space and odesolver in time.
function solve(
problem::BTPDE,
gradient::ScalarGradient,
odesolver::IntervalConstantSolver;
callbacks = [],
)Solve the Bloch-Torrey partial differential equation using P1 finite elements in space and a theta-rule in time. This time stepping scheme requires a degree of implicitness θ and a time step Δt:
θ = 0.5: Crank-Nicolson (second order)θ = 1.0: Implicit Euler (first order)
The function only works for interval-wise constant ScalarGradients, and errors otherwise.
solve(problem::Karger, gradient, odesolver = MagnusGL6(); timestep)Solve the finite pulse Karger model (FPK) using precomputed effective diffusion tensors difftensors.
solve(problem::HADC, gradient::ScalarGradient, odesolver = QNDF();
abstol = 1e-6,
reltol = 1e-4,
)Compute the ADC using a homogenized ADC model (HADC). This is currently only implemented for scalar gradients.
SpinDoctor.solve_multigrad — Functionsolve_multigrad(problem, gradients, args...; kwargs...)Solve problem for multiple magnetic field gradients.
Matrix formalism
SpinDoctor.length2eig — Functionlength2eig(length, D)Convert length scale to Laplace eigenvalue.
SpinDoctor.eig2length — Functioneig2length(λ, D)Convert Laplace eigenvalue to length scale.
SpinDoctor.limit_lengthscale — Functionlimit_lengthscale(lap_eig, λ_max)Only keep modes with length scales larger than minimum
SpinDoctor.compute_mf_diffusion_tensor — Functioncompute_mf_diffusion_tensor(mesh, M, lap_eig, gradient::ScalarGradient)Compute effective diffusion tensors using the matrix formalism.
Utils
SpinDoctor.unitcircle — Functionunitcircle(ndirection; half = false, normal = [0, 0, 1])Create ndirection directions unformly distributed on the unit circle defined by normal.
If half is true, the directions lie on a half-circle instead of the whole circle.
SpinDoctor.unitsphere — Functionunitsphere(ndirection; half = false, normal = [0, 0, 1])Create ndirection directions unformly distributed on the unit sphere.
If half is true, the points will be discributed on the hemisphere defined by normal.
SpinDoctor.compute_signal — Functioncompute_signal(M, ξ)Compute signal from magnetization ξ, using the mass matrix M for integration.
Given a mesh mesh and a vector of compartment mass matrices M_cmpts, a vector of compartment signals may be obtained by compute_signal.(M_cmpts, split_field(mesh, ξ)).
Callbacks
Callbacks are called after every time step when solving the BTPDE.
SpinDoctor.Printer — TypePrinter(; nupdate = 1)Print time stepping information to the console.
SpinDoctor.VTKWriter — TypeVTKWriter(; nupdate = 1, dir = "output", filename = "solution")Write magnetization field to a VTK file after every nupdate timestep. The files are stored in a ParaView data collection file (PVD). The magnetization time series may be visualized in ParaView by opening the file "$dir/$filename.pvd". The compartments are labeled.
SpinDoctor.Plotter — TypePlotter(; nupdate = 1)Plot the evolution of the BTPDE during time stepping. This requires loading the GLMakie plotting backend (]add GLMakie; using GLMakie). The plot is updated every nupdate time step. The resulting figure contains a plot of the time profile, total signal attenuation, and magnetization field (complex magnitude and phase shift).
Recipes
SpinDoctor comes with pre-configures recipes for creating finite element meshes of plates, cylinders, spheres, and neurons.
SpinDoctor.PlateSetup — TypeSetup recipe for a set of stacked plates.
SpinDoctor.CylinderSetup — TypeSetup recipe for a set of cylinders immersed in a ECS.
SpinDoctor.SphereSetup — TypeSetup recipe for a set of spheres immersed in a ECS.
SpinDoctor.NeuronSetup — TypeSetup recipe for a neuron, possibly wrapped in a ECS.
SpinDoctor.coefficients — Functioncoefficients(setup; D, T₂, ρ, κ, γ)Order coefficients compartment arrays.
SpinDoctor.analytical_coefficients — Functionanalytical_coefficients(setup, coeffs)Get coefficients for the analytical module.
SpinDoctor.create_geometry — Functioncreate_geometry(setup; recreate = true)Create cells, surfaces and finite element mesh. If recreate = false, previous geometry will be reused.
This function does the following:
- Check geometry setup consistency
- Create or load cell configuration
- Create or load surface triangulation
- Call TetGen
- Deform domain
- Split mesh into compartments
For custom geometries with more than one compartment, call split_mesh directly instead. This requires facet and element labels.