diff --git a/src/components/abstract_types.jl b/src/components/abstract_types.jl
new file mode 100644
index 0000000000000000000000000000000000000000..7d22ca575746fa593f717b54ce611306045da121
--- /dev/null
+++ b/src/components/abstract_types.jl
@@ -0,0 +1,38 @@
+module AbstracTypes
+import ..Variables: AbstractBlock, duration, variables, effective_time
+
+"""
+Super-type for all individual components that form an MRI sequence (i.e., RF pulse, gradient waveform, or readout event).
+
+RF pulses, instant gradients, and readouts are grouped together into [`EventComponent`](@ref).
+
+These all should have a [`duration`](@ref) in addition to any other relevant [`variables`](@ref).
+"""
+abstract type BaseComponent <: AbstractBlock end
+
+"""
+Super-type for all parts of a gradient waveform.
+
+N should be 1 for a 1D gradient waveform or 3 for a 3D one.
+"""
+abstract type GradientWaveform{N} <: BaseComponent end
+
+
+"""
+Super-type for all RF pulses, instant gradients and readouts that might play out during a gradient waveform.
+
+These all have an [`effective_time`](@ref), which should quantify at what single time one can approximate the RF pulse or readout to have taken place.
+"""
+abstract type EventComponent <: BaseComponent end
+
+"""
+Super type for all RF pulses.
+"""
+abstract type RFPulseComponent <: EventComponent end
+
+"""
+Super type for all readout events.
+"""
+abstract type ReadoutComponent <: EventComponent end
+
+end
\ No newline at end of file
diff --git a/src/components/components.jl b/src/components/components.jl
new file mode 100644
index 0000000000000000000000000000000000000000..ae4561b7d0e2e3be2e40a4eb783131bcc4ff2987
--- /dev/null
+++ b/src/components/components.jl
@@ -0,0 +1,5 @@
+module Components
+include("abstract_types.jl")
+include("gradient_waveforms/gradient_waveforms.jl")
+
+end
\ No newline at end of file
diff --git a/src/components/gradient_waveforms/changing_gradient_blocks.jl b/src/components/gradient_waveforms/changing_gradient_blocks.jl
new file mode 100644
index 0000000000000000000000000000000000000000..608554937a18d54694594159dd5eb97f253cfabf
--- /dev/null
+++ b/src/components/gradient_waveforms/changing_gradient_blocks.jl
@@ -0,0 +1,92 @@
+module ChangingGradientBlocks
+import StaticArrays: SVector
+import ....Variables: VariableType, duration, qvec, bmat_gradient, gradient_strength, slew_rate, get_free_variable
+import ...AbstractTypes: GradientWaveform
+
+
+abstract type ChangingGradient{N} <: GradientWaveform{N} end
+
+"""
+    ChangingGradient1D(grad1, slew_rate, duration)
+
+Underlying type for any linearly changing part in a 1D gradient waveform.
+
+Usually, you do not want to create this object directly, use a `BuildingBlock` instead.
+"""
+struct ChangingGradient1D <: ChangingGradient{1}
+    gradient_strength_start :: VariableType
+    slew_rate :: VariableType
+    duration :: VariableType
+end
+
+"""
+    ChangingGradient3D(grad1, slew_rate, duration)
+
+Underlying type for any linearly changing part in a 3D gradient waveform.
+
+Usually, you do not want to create this object directly, use a `BuildingBlock` instead.
+"""
+struct ChangingGradient3D <: ChangingGradient{3}
+    gradient_strength_start :: SVector{3, <:VariableType}
+    slew_rate :: SVector{3, <:VariableType}
+    duration :: VariableType
+end
+
+
+duration(cgb::ChangingGradient) = cgb.duration
+
+grad_start(cgb::ChangingGradient) = cgb.gradient_strength_start
+slew_rate(cgb::ChangingGradient) = cgb.slew_rate
+grad_end(cgb::ChangingGradient) = grad_start(cgb) .+ slew_rate(cgb) .* duration(cgb)
+gradient_strength(cgb::ChangingGradient) = max.(grad_start(cgb), grad_end(cgb))
+qvec(cgb::ChangingGradient) = (grad_start(cgb) .+ grad_end(cgb)) .* (duration(cgb) * π)
+
+_mult(g1::VariableType, g2::VariableType) = g1 * g2
+_mult(g1::AbstractVector, g2::AbstractVector) = g1 .* permutedims(g2)
+
+function bmat_gradient(cgb::ChangingGradientBlock, qstart)
+    # grad = (g1 * (duration - t) + g2 * t) / duration
+    #      = g1 + (g2 - g1) * t / duration
+    # q = qstart + g1 * t + (g2 - g1) * t^2 / (2 * duration)
+    # \int dt (qstart + t * grad)^2 = 
+    #   qstart^2 * duration +
+    #   qstart * g1 * duration^2 +
+    #   qstart * (g2 - g1) * duration^2 / 3 +
+    #   g1^2 * duration^3 / 3 +
+    #   g1 * (g2 - g1) * duration^3 / 4 +
+    #   (g2 - g1)^2 * duration^3 / 10
+    return (
+        _mult(qstart, qstart) .* duration(cgb) .+
+        duration(cgb)^2 .* _mult(qstart, 2 .* grad_start(cgb) .+ grad_end(cgb)) .* 2Ï€ ./ 3 .+
+        bmat_gradient(cgb)
+    )
+end
+
+function bmat_gradient(cgb::ChangingGradientBlock)
+    diff = slew_rate(cgb) .* duration(cgb)
+    return (2Ï€)^2 .* (
+        _mult(grad_start(cgb), grad_start(cgb)) ./ 3 .+
+        _mult(grad_start(cgb), diff) ./ 4 .+
+        _mult(diff, diff) ./ 10
+    ) .* duration(cgb)^3
+end
+
+
+"""
+    split_gradient(constant/changing_gradient_block, times...)
+
+Split a single gradient at a given times.
+
+All times are relative to the start of the gradient block (in ms).
+Times are assumed to be in increasing order and between 0 and the duration of the gradient block.
+
+For N times this returns a vector with the N+1 replacement [`ConstantGradientBlock`](@ref) or [`ChangingGradientBlock`](@ref) objects.
+"""
+function split_gradient(cgb::ChangingGradient, times::VariableType...)
+    all_times = [0., times...]
+    durations = [times[1], [t[2] - t[1] for t in zip(times[1:end-1], times[2:end])]..., duration(cgb) - times[end]]
+    return [cls(cgb)(cgb.gradient_strength .+ cgb.slew_rate .* t, cgb.slew_rate, d) for (t, d) in zip(all_times, durations)]
+end
+
+
+end
diff --git a/src/components/gradient_waveforms/constant_gradient_blocks.jl b/src/components/gradient_waveforms/constant_gradient_blocks.jl
new file mode 100644
index 0000000000000000000000000000000000000000..5efffb4fc3ad3de444abe939f15d05d19aabc0be
--- /dev/null
+++ b/src/components/gradient_waveforms/constant_gradient_blocks.jl
@@ -0,0 +1,66 @@
+module ConstantGradientBlocks
+import StaticArrays: SVector
+import ....Variables: VariableType, duration, qvec, bmat_gradient, gradient_strength, slew_rate, get_free_variable
+import ...AbstractTypes: GradientWaveform
+
+abstract type ConstantGradient{N} <: GradientWaveform{N} end
+
+"""
+    ConstantGradient1D(gradient_strength, duration)
+
+Underlying type for any flat part in a 1D gradient waveform.
+
+Usually, you do not want to create this object directly, use a `BuildingBlock` instead.
+"""
+struct ConstantGradient1D <: ConstantGradient{1}
+    gradient_strength :: VariableType
+    duration :: VariableType
+end
+
+"""
+    ConstantGradient1D(gradient_strength, duration)
+
+Underlying type for any flat part in a 3D gradient waveform.
+
+Usually, you do not want to create this object directly, use a `BuildingBlock` instead.
+"""
+struct ConstantGradient3D <: ConstanGradient{3}
+    gradient_strength :: SVector{3, <:VariableType}
+    duration :: VariableType
+end
+
+duration(cgb::ConstantGradient) = cgb.duration
+gradient_strength(cgb::ConstantGradient) = cgb.gradient_strength
+slew_rate(::ConstantGradient1D) = 0.
+slew_rate(::ConstantGradient3D) = zero(SVector{3, Float64})
+qvec(cgb::ConstantGradient1D) = duration(cgb) * gradient_strength(cgb) * 2Ï€
+qvec(cgb::ConstantGradient3D) = @. duration(cgb) * gradient_strength(cgb) * 2Ï€
+
+_mult(g1::VariableType, g2::VariableType) = g1 * g2
+_mult(g1::AbstractVector, g2::AbstractVector) = g1 .* permutedims(g2)
+
+function bmat_gradient(cgb::ConstantGradient)
+    grad = 2Ï€ .* gradient_strength(cgb)
+    return _mult(grad, grad) .* duration(cgb)^3 ./3
+end
+
+function bmat_gradient(cgb::ConstantGradient, qstart)
+    # \int dt (qstart + t * grad)^2 = 
+    #   qstart^2 * duration +
+    #   qstart * grad * duration^2 +
+    #   grad * grad * duration^3 / 3 +
+    grad = 2Ï€ .* gradient_strength(cgb)
+    return (
+        _mult(qstart, qstart) .* duration(cgb) .+
+        _mult(qstart, grad) .* duration(cgb)^2 .+
+        bmat_gradient(cgb)
+    )
+end
+
+function split_gradient(cgb::ConstantGradient, times::VariableType...)
+    durations = [times[1], [t[2] - t[1] for t in zip(times[1:end-1], times[2:end])]..., duration(cgb) - times[end]]
+    @assert all(durations >= 0.)
+    return [typeof(cgb)(cgb.gradient_strength, d) for d in durations]
+end
+
+end
diff --git a/src/components/gradient_waveforms/gradient_waveforms.jl b/src/components/gradient_waveforms/gradient_waveforms.jl
new file mode 100644
index 0000000000000000000000000000000000000000..52705582379a82d49d6c9b12836231f7a086ebde
--- /dev/null
+++ b/src/components/gradient_waveforms/gradient_waveforms.jl
@@ -0,0 +1,29 @@
+"""
+Module defining sub-types of the [`GradientWaveform`](@ref).
+
+There are only three types of [`GradientBlock`] objects:
+- [`ChangingGradientBlock`](@ref): any gradient changing linearly in strength.
+- [`ConstantGradientBlock`](@ref): any gradient staying constant in strength. These can overlap with a pulse (`SliceSelectPulse`).
+- [`NoGradientBlock`](@ref): any part of the gradient waveform when no gradient is active.
+
+These parts are combined into a full gradient waveform in a `BuildingBlock`.
+
+Each part of this gradient waveform can compute:
+- [`gradient_strength`]: maximum gradient strength in each dimension.
+- [`slew_rate`]: maximum slew rate in each dimension.
+- [`qvec`]: area under curve in each dimension
+- [`bmat_gradient`]: diffusion weighting (scalar in 1D or matrix in 3D).
+"""
+module Gradients
+
+include("changing_gradient_blocks.jl")
+include("constant_gradient_blocks.jl")
+include("no_gradient_blocks.jl")
+
+
+import ..AbstractTypes: GradientWaveform
+import .NoGradientBlocks: NoGradientBlock
+import .ChangingGradientBlocks: ChangingGradientBlock, split_gradient
+import .ConstantGradientBlocks: ConstantGradientBlock
+
+end
\ No newline at end of file
diff --git a/src/gradients/instant_gradients.jl b/src/components/gradient_waveforms/instant_gradients.jl
similarity index 100%
rename from src/gradients/instant_gradients.jl
rename to src/components/gradient_waveforms/instant_gradients.jl
diff --git a/src/components/gradient_waveforms/no_gradient_blocks.jl b/src/components/gradient_waveforms/no_gradient_blocks.jl
new file mode 100644
index 0000000000000000000000000000000000000000..3552f22477d82fa05346549d1a9b119b9f0d4849
--- /dev/null
+++ b/src/components/gradient_waveforms/no_gradient_blocks.jl
@@ -0,0 +1,43 @@
+module NoGradientBlocks
+import StaticArrays: SVector, SMatrix
+import ....Variables: VariableType, duration, qvec, bmat_gradient, gradient_strength, slew_rate, get_free_variable
+import ...AbstractTypes: GradientWaveform
+import ..ChangingGradientBlocks: split_gradient
+
+"""
+    NoGradientBlock(duration)
+
+Part of a gradient waveform when there is no gradient active.
+
+Usually, you do not want to create this object directly, use a `BuildingBlock` instead.
+"""
+struct NoGradientBlock{N} <: GradientWaveform{N}
+    duration :: VariableType
+    function NoGradientBlock{N}(duration)
+        if !(N in (1, 3))
+            error("Dimensionality of the gradient should be 1 or 3, not $N")
+        end
+        new(duration)
+    end
+end
+
+duration(ngb::NoGradientBlock) = duration(ngb)
+for func in (:qvec, :gradient_strength, :slew_rate)
+    @eval $func(::NoGradientBlock{1}) = 0.
+    @eval $func(::NoGradientBlock{3}) = zero(SVector{3, Float64})
+end
+
+bmat_gradient(::NoGradientBlock{1}) = 0.
+bmat_gradient(::NoGradientBlock{3}) = zero(SMatrix{3, 3, Float64, 9})
+
+bmat_gradient(::NoGradientBlock, ) = 0.
+bmat_gradient(ngb::NoGradientBlock{1}, qstart::VariableType) = qstart^2 * duration(ngb)
+bmat_gradient(ngb::NoGradientBlock{3}, qstart::AbstractVector{<:VariableType}) = @. qstart * permutedims(qstart) * duration(ngb)
+
+function split_gradient(ngb::NoGradientBlock, times::VariableType...)
+    durations = [times[1], [t[2] - t[1] for t in zip(times[1:end-1], times[2:end])]..., duration(ngb) - times[end]]
+    @assert all(durations >= 0.)
+    return [NoGradientBlock(d) for d in durations]
+end
+
+end
\ No newline at end of file
diff --git a/src/gradients/changing_gradient_blocks.jl b/src/gradients/changing_gradient_blocks.jl
deleted file mode 100644
index b10699bf1e9e666d8daa5bba1a8433790a1dc499..0000000000000000000000000000000000000000
--- a/src/gradients/changing_gradient_blocks.jl
+++ /dev/null
@@ -1,83 +0,0 @@
-module ChangingGradientBlocks
-import StaticArrays: SVector
-import ...Variables: VariableType, variables, get_free_variable
-import ...BuildingBlocks: GradientBlock
-import ...Variables: qvec, bmat_gradient, gradient_strength, slew_rate, duration, variables, VariableType
-import ...BuildingBlocks: GradientBlock, RFPulseBlock
-
-"""
-    ChangingGradientBlock(grad1, slew_rate, duration, rotate, scale)
-
-Underlying type for any linearly changing part in a gradient waveform.
-
-Usually, you do not want to create this object directly, use a gradient waveform instead.
-
-## Parameters
-- `rotate`: with which user-set parameter will this gradient be rotated (e.g., :bvec). Default is no rotation.
-- `scale`: with which user-set parameter will this gradient be scaled (e.g., :bval). Default is no scaling.
-"""
-struct ChangingGradientBlock <: GradientBlock
-    gradient_strength_start :: SVector{3, <:VariableType}
-    slew_rate :: SVector{3, <:VariableType}
-    duration :: VariableType
-    rotate :: Union{Nothing, Symbol}
-    scale :: Union{Nothing, Symbol}
-end
-
-
-duration(cgb::ChangingGradientBlock) = cgb.duration
-
-grad_start(cgb::ChangingGradientBlock) = cgb.gradient_strength_start
-slew_rate(cgb::ChangingGradientBlock) = cgb.slew_rate
-grad_end(cgb::ChangingGradientBlock) = grad_start(cgb) .+ slew_rate(cgb) .* duration(cgb)
-gradient_strength(cgb::ChangingGradientBlock) = max.(grad_start(cgb), grad_end(cgb))
-qvec(cgb::ChangingGradientBlock) = (grad_start(cgb) .+ grad_end(cgb)) .* (duration(cgb) * π)
-
-function bmat_gradient(cgb::ChangingGradientBlock, qstart)
-    # grad = (g1 * (duration - t) + g2 * t) / duration
-    #      = g1 + (g2 - g1) * t / duration
-    # q = qstart + g1 * t + (g2 - g1) * t^2 / (2 * duration)
-    # \int dt (qstart + t * grad)^2 = 
-    #   qstart^2 * duration +
-    #   qstart * g1 * duration^2 +
-    #   qstart * (g2 - g1) * duration^2 / 3 +
-    #   g1^2 * duration^3 / 3 +
-    #   g1 * (g2 - g1) * duration^3 / 4 +
-    #   (g2 - g1)^2 * duration^3 / 10
-    return (
-        qstart .* permutedims(qstart) .* duration(cgb) .+
-        duration(cgb)^2 .* qstart .* permutedims(
-            2 .* grad_start(cgb) .+
-            grad_end(cgb)) .* 2Ï€ ./ 3 .+
-        bmat_gradient(cgb)
-    )
-end
-
-function bmat_gradient(cgb::ChangingGradientBlock)
-    diff = slew_rate(cgb) .* duration(cgb)
-    return (2Ï€)^2 .* (
-        grad_start(cgb) .* permutedims(grad_start(cgb)) ./ 3 .+
-        grad_start(cgb) .* permutedims(diff) ./ 4 .+
-        diff .* permutedims(diff) ./ 10
-    ) .* duration(cgb)^3
-end
-
-
-"""
-    split_gradient(constant/changing_gradient_block, times...)
-
-Split a single gradient at a given times.
-
-All times are relative to the start of the gradient block (in ms).
-Times are assumed to be in increasing order and between 0 and the duration of the gradient block.
-
-For N times this returns a vector with the N+1 replacement [`ConstantGradientBlock`](@ref) or [`ChangingGradientBlock`](@ref) objects.
-"""
-function split_gradient(cgb::ChangingGradientBlock, times::VariableType...)
-    all_times = [0., times...]
-    durations = [times[1], [t[2] - t[1] for t in zip(times[1:end-1], times[2:end])]..., duration(cgb) - times[end]]
-    return [ChangingGradientBlock(cgb.gradient_strength .+ cgb.slew_rate .* t, cgb.slew_rate, d, cgb.rotate, cgb.scale) for (t, d) in zip(all_times, durations)]
-end
-
-
-end
diff --git a/src/gradients/constant_gradient_blocks.jl b/src/gradients/constant_gradient_blocks.jl
deleted file mode 100644
index eaf7e1c3f113db807ba40061242fa29f3ff68d70..0000000000000000000000000000000000000000
--- a/src/gradients/constant_gradient_blocks.jl
+++ /dev/null
@@ -1,56 +0,0 @@
-module ConstantGradientBlocks
-import StaticArrays: SVector
-import ...Variables: VariableType, variables
-import ...BuildingBlocks: GradientBlock
-import ...Variables: qvec, bmat_gradient, gradient_strength, slew_rate, duration, variables, VariableType
-import ...BuildingBlocks: GradientBlock, RFPulseBlock
-import ..ChangingGradientBlocks: split_gradient
-
-"""
-    ConstantGradientBlock(gradient_strength, duration, rotate, scale)
-
-Underlying type for any flat part in a gradient waveform.
-It can overlap with an [`RFPulseBlock`], in which case it will be a `SliceSelectPulse`
-
-Usually, you do not want to create this object directly, use a gradient waveform instead.
-
-## Parameters
-- `rotate`: with which user-set parameter will this gradient be rotated (e.g., :bvec). Default is no rotation.
-- `scale`: with which user-set parameter will this gradient be scaled (e.g., :bval). Default is no scaling.
-"""
-struct ConstantGradientBlock <: GradientBlock
-    gradient_strength :: SVector{3, <:VariableType}
-    duration :: VariableType
-    rotate :: Union{Nothing, Symbol}
-    scale :: Union{Nothing, Symbol}
-end
-
-duration(cgb::ConstantGradientBlock) = cgb.duration
-gradient_strength(cgb::ConstantGradientBlock) = cgb.gradient_strength
-slew_rate(::ConstantGradientBlock) = zero(SVector{3, Float64})
-qvec(cgb::ConstantGradientBlock) = duration(cgb) .* gradient_strength(cgb) .* 2Ï€
-
-function bmat_gradient(cgb::ConstantGradientBlock)
-    grad = 2Ï€ .* gradient_strength(cgb)
-    return (grad .* permutedims(grad)) .* duration(cgb)^3 ./3
-end
-
-function bmat_gradient(cgb::ConstantGradientBlock, qstart)
-    # \int dt (qstart + t * grad)^2 = 
-    #   qstart^2 * duration +
-    #   qstart * grad * duration^2 +
-    #   grad * grad * duration^3 / 3 +
-    grad = 2Ï€ .* gradient_strength(cgb)
-    return (
-        qstart .* permutedims(qstart) .* duration(cgb) .+
-        qstart .* permutedims(grad) .* duration(cgb)^2 .+
-        bmat_gradient(cgb)
-    )
-end
-
-function split_gradient(cgb::ConstantGradientBlock, times::VariableType...)
-    durations = [times[1], [t[2] - t[1] for t in zip(times[1:end-1], times[2:end])]..., duration(cgb) - times[end]]
-    return [ConstantGradientBlock(cgb.gradient_strength, d, cgb.rotate, cgb.scale) for d in durations]
-end
-
-end
diff --git a/src/gradients/gradients.jl b/src/gradients/gradients.jl
deleted file mode 100644
index e5653e2b83a3e30005e900e24a6eff328cbfc4af..0000000000000000000000000000000000000000
--- a/src/gradients/gradients.jl
+++ /dev/null
@@ -1,22 +0,0 @@
-"""
-Module defining sub-types of the [`GradientBlock`](@ref), i.e., any [`BuildingBlock`](@ref) that only defines a gradient profile.
-
-There are only three types of [`GradientBlock`] objects:
-- [`ChangingGradientBlock`](@ref): any gradient changing linearly in strength.
-- [`ConstantGradientBlock`](@ref): any gradient staying constant in strength. These can overlap with a pulse (`SliceSelectPulse`).
-- [`InstantGradientBlock`](@ref): an infinitely short gradient pulse.
-
-Combinations of these should sub-type from [`SpecificWaveform`](@ref).
-"""
-module Gradients
-
-include("changing_gradient_blocks.jl")
-include("constant_gradient_blocks.jl")
-include("instant_gradients.jl")
-
-
-import ..BuildingBlocks: GradientBlock
-import .ChangingGradientBlocks: ChangingGradientBlock, split_gradient
-import .ConstantGradientBlocks: ConstantGradientBlock
-import .InstantGradients: InstantGradientBlock
-end
\ No newline at end of file
diff --git a/src/variables.jl b/src/variables.jl
index c1d971c55c12470869e13688b4f59dd1f7ac9d35..ada87ea321a1c115c4d2edc756ca678f3f091f75 100644
--- a/src/variables.jl
+++ b/src/variables.jl
@@ -1,8 +1,21 @@
+"""
+Defines the functions that can be called on parts of an MRI sequence to query or constrain any variables.
+
+In addition this defines:
+- [`variables`](@ref): dictionary containing all variables.
+- [`VariableType`](@ref): parent type for any variables (whether number or JuMP variable).
+- [`get_free_variable`](@ref): helper function to create new JuMP variables.
+"""
 module Variables
 import JuMP: @variable, Model, @objective, objective_function, value, AbstractJuMPScalar
 import ..Scanners: gradient_strength, slew_rate
 import ..BuildSequences: global_model
 
+"""
+Parent type of all components, building block, and sequences that form an MRI sequence.
+"""
+abstract type AbstractBlock end
+
 all_variables_symbols = [
     :block => [
         :duration => "duration of the building block in ms.",
@@ -10,7 +23,6 @@ all_variables_symbols = [
     :sequence => [
         :TR => "Time on which an MRI sequence repeats itself in ms.",
     ],
-
     :pulse => [
         :flip_angle => "The flip angle of the RF pulse in degrees",
         :amplitude => "The maximum amplitude of an RF pulse in kHz",
@@ -23,8 +35,6 @@ all_variables_symbols = [
         :slice_thickness => "Slice thickness of an RF pulse that is active during a gradient in mm. To set constraints it is often better to use [`inverse_slice_thickness`](@ref).",
         :inverse_slice_thickness => "Inverse of slice thickness of an RF pulse that is active during a gradient in 1/mm. Also, see [`slice_thickness`](@ref).",
     ],
-
-    # gradients
     :gradient => [
         :qvec => "The spatial range and orientation on which the displacements can be detected due to this gradient in rad/um.",
         :qval => "The spatial range on which the displacements can be detected due to this gradient in rad/um (i.e., norm of [`qvec`](@ref)). To set constraints it is often better to use [`qvec`](@ref) or [`qval_square`](@ref).",
@@ -48,10 +58,12 @@ all_variables_symbols = [
     ]
 ]
 
+"""
+Collection of all functions that return variables that can be used to query or constrain their values.
+"""
 variables = Dict{Symbol, Function}()
 
 
-
 for (block_symbol, all_functions) in all_variables_symbols
     for (func_symbol, description) in all_functions
         as_string = "    $func_symbol($block_symbol)\n\n$description\n\nThis represents a variable within the sequence. Variables can be set during the construction of a [`BuildingBlock`](@ref) or used to create constraints after the fact."
@@ -65,6 +77,11 @@ for (block_symbol, all_functions) in all_variables_symbols
 end
 
 
+"""
+Dictionary with alternative versions of specific function.
+    
+Setting constraints on these alternative functions can be helpful as it avoids some operations, which the optimiser might struggle with.
+"""
 alternative_variables = Dict(
     qval => (qval_square, n->n^2, sqrt, false),
     slice_thickness => (inverse_slice_thickness, inv, inv, true),
@@ -74,12 +91,16 @@ alternative_variables = Dict(
 )
 
 
-# These functions are more fully defined in building_blocks.jl
-function start_time end
-function end_time end
-function effective_time end
+"""
+Parent type for any variable in the MRI sequence.
 
+Each variable can be one of:
+- a new JuMP variable
+- an expression linking this variable to other JuMP variable
+- a number
 
+Create these using [`get_free_variable`](@ref).
+"""
 const VariableType = Union{Number, AbstractJuMPScalar}
 
 
@@ -87,6 +108,8 @@ const VariableType = Union{Number, AbstractJuMPScalar}
     get_free_variable(value; integer=false)
 
 Get a representation of a given `variable` given a user-defined constraint.
+
+The result is guaranteed to be a [`VariableType`](@ref).
 """
 get_free_variable(value::Number; integer=false) = integer ? Int(value) : Float64(value)
 get_free_variable(value::VariableType; integer=false) = value