Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tutorial for SimpleMotion creation and simulation #387

Merged
merged 58 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
cbda572
Initial
pvillacorta May 1, 2024
dfc6356
Solve rotation bug in `displacement_z`
pvillacorta May 1, 2024
c812ab1
Default `max_spins` to 100000
pvillacorta May 1, 2024
5cec38d
Improve example
pvillacorta May 1, 2024
d710be8
Solve bugs
pvillacorta May 1, 2024
fb5655c
Comment all tests for easy docs preview
pvillacorta May 1, 2024
cc42132
Revert comments
pvillacorta May 1, 2024
41a66b7
Merge branch 'docs-simplemotion' of https://github.com/JuliaHealth/Ko…
pvillacorta May 1, 2024
04c7c2f
Revert comments
pvillacorta May 1, 2024
fd5476d
Merge branch 'docs-simplemotion' of https://github.com/JuliaHealth/Ko…
pvillacorta May 1, 2024
86bb9e4
Revert comments
pvillacorta May 1, 2024
adf5678
Comment CI
pvillacorta May 1, 2024
4e5a42e
Revert comment
pvillacorta May 1, 2024
435fbe4
Minor change
pvillacorta May 1, 2024
9f24700
Merge branch 'master' into docs-simplemotion
cncastillo May 2, 2024
5fc9305
Correct bugs in lit-05-SimpleMotion.jl
pvillacorta May 2, 2024
2a0bb1b
Solve another bug
pvillacorta May 2, 2024
a0d5835
Merge branch 'master' into docs-simplemotion
cncastillo May 5, 2024
0cd06a1
Fix literate badges
cncastillo May 5, 2024
8062bdb
Add InteractiveUtils compat to KomaFiles
cncastillo May 5, 2024
592b85f
Small changes in plot_phantom_map
cncastillo May 5, 2024
f8ae5df
Changes in motion example
cncastillo May 5, 2024
6711697
Removed some boilerplate
cncastillo May 5, 2024
6e74e2e
Syntax highlight in docs
cncastillo May 5, 2024
6f409c5
Minor modifications
cncastillo May 5, 2024
62ee462
Syntax hightlight docs
cncastillo May 5, 2024
58d0ae0
Change figure size
cncastillo May 5, 2024
8534e80
Updated motion example
cncastillo May 5, 2024
bc1b615
Plain text citation, now copy-pastable
cncastillo May 5, 2024
1534ddc
Organize make
cncastillo May 6, 2024
049a0ca
removed node dep
cncastillo May 6, 2024
125c81a
update simple motion example
cncastillo May 6, 2024
8293ca1
Improve SimpleMotion example
pvillacorta May 28, 2024
4252bf4
Minor changes in Simple Motion tutorial
pvillacorta May 28, 2024
f914592
Minor change
pvillacorta May 28, 2024
bbdb64b
Docstrings
pvillacorta May 29, 2024
7aec83e
Minor changes
pvillacorta May 30, 2024
255090c
Try adding image into docstring
pvillacorta May 30, 2024
e025248
Minor change
pvillacorta May 30, 2024
7cdf263
Fix LaTex syntax
pvillacorta May 30, 2024
871ed6a
Fix scape character
pvillacorta May 30, 2024
08bc2a5
Remove comment
pvillacorta May 30, 2024
6195c1c
Merge branch 'master' into docs-simplemotion
pvillacorta May 30, 2024
1e22d54
Try reducing image size
pvillacorta May 31, 2024
b5d22fc
Merge branch 'docs-simplemotion' of https://github.com/JuliaHealth/Ko…
pvillacorta May 31, 2024
0565ce3
Fix reducing image size
pvillacorta May 31, 2024
5913158
Requested changes
pvillacorta May 31, 2024
3c2c8c5
Fix table does not work on docstrings
pvillacorta May 31, 2024
378664a
Change LaTex equation syntax
pvillacorta Jun 2, 2024
58cd73b
Change explanation
pvillacorta Jun 2, 2024
9460d17
Change explanation
pvillacorta Jun 2, 2024
eeea1dc
Change explanation
pvillacorta Jun 2, 2024
662c0fb
Change explanation
pvillacorta Jun 2, 2024
9eedd1d
Minor change in docstring
pvillacorta Jun 2, 2024
4223e7c
Reduce figure size
pvillacorta Jun 2, 2024
099080c
Remove explanation from API docs, leave for "Explanations"
pvillacorta Jun 2, 2024
f3df812
Merge branch 'master' into docs-simplemotion
pvillacorta Jun 2, 2024
f6074ec
Δx -> ux(t) in plot
pvillacorta Jun 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions KomaMRIBase/src/datatypes/Phantom.jl
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,7 @@ Base.:(≈)(obj1::Phantom, obj2::Phantom) = reduce(&, [getfield(obj1, field
Base.:(==)(m1::MotionModel, m2::MotionModel) = false
Base.:(≈)(m1::MotionModel, m2::MotionModel) = false

"""
Separate object spins in a sub-group
"""
"""Separate object spins in a sub-group"""
Base.getindex(obj::Phantom, p::Union{AbstractRange,AbstractVector,Colon}) = begin
fields = []
for field in Iterators.filter(x -> !(x == :name), fieldnames(Phantom))
Expand Down Expand Up @@ -110,9 +108,7 @@ end
return obj1
end

"""
dims = get_dims(obj)
"""
"""dims = get_dims(obj)"""
function get_dims(obj::Phantom)
dims = Bool[]
push!(dims, any(x -> x != 0, obj.x))
Expand All @@ -128,7 +124,7 @@ end
"""
obj = heart_phantom(...)

Heart-like LV phantom. The variable `circumferential_strain` and `radial_strain` are for streching (if positive)
Heart-like LV 2D phantom. The variable `circumferential_strain` and `radial_strain` are for streching (if positive)
or contraction (if negative). `rotation_angle` is for rotation.

# Arguments
Expand Down
50 changes: 39 additions & 11 deletions KomaMRIBase/src/datatypes/phantom/motion/ArbitraryMotion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,37 @@ const LinearInterpolator = Interpolations.Extrapolation{
} where {T<:Real,V<:AbstractVector{T}}

"""
Arbitrary Motion
motion = ArbitraryMotion(period_durations, dx, dy, dz)

x = x + ux
ArbitraryMotion model. For this motion model, it is necessary to define
motion for each spin independently, in x (`dx`), y (`dy`) and z (`dz`).
`dx`, `dy` and `dz` are three matrixes, of (``N_{spins}`` x ``N_{discrete\\,times}``) each.
This means that each row corresponds to a spin trajectory over a set of discrete time instants.
`period_durations` is a vector that contains the period for periodic (one element) or
pseudo-periodic (two or more elements) motion.
The discrete time instants are calculated diving `period_durations` by ``N_{discrete\\,times}``.

This motion model is useful for defining arbitrarly complex motion, specially
for importing the spin trajectories from another source, like XCAT or a CFD.

# Arguments
- `period_durations`: (`Vector{T}`)
- `dx`: (`::Array{T,2}`) matrix for displacements in x
- `dy`: (`::Array{T,2}`) matrix for displacements in y
- `dz`: (`::Array{T,2}`) matrix for displacements in z

# Returns
- `motion`: (`::ArbitraryMotion`) ArbitraryMotion struct

# Examples
```julia-repl
julia> motion = ArbitraryMotion(
[1.0],
0.01.*rand(1000, 10),
0.01.*rand(1000, 10),
0.01.*rand(1000, 10)
)
```
"""
struct ArbitraryMotion{T<:Real,V<:AbstractVector{T}} <: MotionModel{T}
period_durations::Vector{T}
Expand All @@ -27,27 +55,27 @@ end

function ArbitraryMotion(
period_durations::AbstractVector{T},
Δx::AbstractArray{T,2},
Δy::AbstractArray{T,2},
Δz::AbstractArray{T,2},
dx::AbstractArray{T,2},
dy::AbstractArray{T,2},
dz::AbstractArray{T,2},
) where {T<:Real}
@warn "Note that ArbitraryMotion is under development so it is not optimized so far" maxlog = 1
Ns = size(Δx)[1]
num_pieces = size(Δx)[2] + 1
Ns = size(dx)[1]
num_pieces = size(dx)[2] + 1
limits = times(period_durations, num_pieces)

#! format: off
Δ = zeros(Ns,length(limits),4)
Δ[:,:,1] = hcat(repeat(hcat(zeros(Ns,1),Δx),1,length(period_durations)),zeros(Ns,1))
Δ[:,:,2] = hcat(repeat(hcat(zeros(Ns,1),Δy),1,length(period_durations)),zeros(Ns,1))
Δ[:,:,3] = hcat(repeat(hcat(zeros(Ns,1),Δz),1,length(period_durations)),zeros(Ns,1))
Δ[:,:,1] = hcat(repeat(hcat(zeros(Ns,1),dx),1,length(period_durations)),zeros(Ns,1))
Δ[:,:,2] = hcat(repeat(hcat(zeros(Ns,1),dy),1,length(period_durations)),zeros(Ns,1))
Δ[:,:,3] = hcat(repeat(hcat(zeros(Ns,1),dz),1,length(period_durations)),zeros(Ns,1))

etpx = [extrapolate(interpolate((limits,), Δ[i,:,1], Gridded(Linear())), Periodic()) for i in 1:Ns]
etpy = [extrapolate(interpolate((limits,), Δ[i,:,2], Gridded(Linear())), Periodic()) for i in 1:Ns]
etpz = [extrapolate(interpolate((limits,), Δ[i,:,3], Gridded(Linear())), Periodic()) for i in 1:Ns]
#! format: on

return ArbitraryMotion(period_durations, Δx, Δy, Δz, etpx, etpy, etpz)
return ArbitraryMotion(period_durations, dx, dy, dz, etpx, etpy, etpz)
end

function Base.getindex(
Expand Down
19 changes: 16 additions & 3 deletions KomaMRIBase/src/datatypes/phantom/motion/SimpleMotion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,25 @@ is_composable(motion_type::SimpleMotionType{T}) where {T<:Real} = false
"""
motion = SimpleMotion(types)

SimpleMotion model
SimpleMotion model. It allows for the definition of motion by means of simple parameters.
The `SimpleMotion` struct is composed by only one field, called `types`,
which is a vector of simple motion types. This vector will contain as many elements
as simple motions we want to combine.

# Arguments
- `types`: (`::Vector{<:SimpleMotionType{T}}`) vector of simple motion types

# Returns
- `motion`: (`::SimpleMotion`) SimpleMotion struct

# Examples
```julia-repl
julia> motion = SimpleMotion([
Translation(dx=0.01, dy=0.02, dz=0.0, t_start=0.0, t_end=0.5),
Rotation(pitch=15.0, roll=0.0, yaw=20.0, t_start=0.1, t_end=0.5),
HeartBeat(circumferential_strain=-0.3, radial_strain=-0.2, longitudinal_strain=0.0, t_start=0.2, t_end=0.5)
])
```
"""
struct SimpleMotion{T<:Real} <: MotionModel{T}
types::Vector{<:SimpleMotionType{T}}
Expand All @@ -39,9 +51,10 @@ Base.:(==)(t1::SimpleMotionType, t2::SimpleMotionType) = false
Base.:(≈)(t1::SimpleMotionType, t2::SimpleMotionType) = false

"""
x, y, z = het_spin_coords(motion, x, y, z, t')
x, y, z = get_spin_coords(motion, x, y, z, t)

Calculates the position of each spin at a set of arbitrary time instants, i.e. the time steps of the simulation.
For each dimension (x, y, z), the output matrix has ``N_{\text{spins}}`` rows and `length(t)` columns.

# Arguments
- `motion`: (`::MotionModel`) phantom motion
Expand All @@ -51,7 +64,7 @@ Calculates the position of each spin at a set of arbitrary time instants, i.e. t
- `t`: (`::AbstractArray{T<:Real}`) horizontal array of time instants

# Returns
- `z, y, z`: (`::Tuple{AbstractArray, AbstractArray, AbstractArray}`) spin positions over time
- `x, y, z`: (`::Tuple{AbstractArray, AbstractArray, AbstractArray}`) spin positions over time
"""
function get_spin_coords(
motion::SimpleMotion{T},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ HeartBeat struct. It produces a heartbeat-like motion, characterised by three ty
Circumferential, Radial and Longitudinal

# Arguments
- `circumferential_strain`: (`::Real`, `=-0.3`) contraction parameter
- `radial_strain`: (`::Real`, `=-0.3`) contraction parameter
- `longitudinal_strain`: (`::Real`, `=1`) contraction parameter
- `circumferential_strain`: (`::Real`) contraction parameter
- `radial_strain`: (`::Real`) contraction parameter
- `longitudinal_strain`: (`::Real`) contraction parameter
- `t_start`: (`::Real`, `[s]`) initial time
- `t_end`: (`::Real`, `[s]`) final time

# Returns
- `heartbeat`: (`::HeartBeat`) HeartBeat struct

# Examples
```julia-repl
julia> hb = HeartBeat(circumferential_strain=-0.3, radial_strain=-0.2, longitudinal_strain=0.0, t_start=0.2, t_end=0.5)
```
"""
@with_kw struct HeartBeat{T<:Real} <: SimpleMotionType{T}
circumferential_strain :: T
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ HeartBeat struct. It produces a heartbeat-like motion, characterised by three ty
Circumferential, Radial and Longitudinal

# Arguments
- `circumferential_strain`: (`::Real`, `=-0.3`) contraction parameter
- `radial_strain`: (`::Real`, `=-0.3`) contraction parameter
- `longitudinal_strain`: (`::Real`, `=1`) contraction parameter
- `circumferential_strain`: (`::Real`) contraction parameter
- `radial_strain`: (`::Real`) contraction parameter
- `longitudinal_strain`: (`::Real`) contraction parameter
- `period`: (`::Real`, `[s]`) period
- `asymmetry`: (`::Real`) asymmetry factor, between 0 and 1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ function displacement_z(
α = t_unit .* motion_type.pitch
β = t_unit .* motion_type.roll
γ = t_unit .* motion_type.yaw
return -sind.(β) .* x + cosd.(β) .* sind.(α) .* y .- z
return -sind.(β) .* x +
cosd.(β) .* sind.(α) .* y +
cosd.(β) .* cosd.(α) .* z .- z
end

function times(motion_type::PeriodicRotation)
Expand Down
112 changes: 51 additions & 61 deletions KomaMRIBase/src/datatypes/phantom/motion/simplemotion/Rotation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,39 @@
rotation = Rotation(t_start, t_end, pitch, roll, yaw)

Rotation motion struct. It produces a rotation of the phantom in the three axes:
x (pitch), y (roll), and z (yaw)
x (pitch), y (roll), and z (yaw).
We follow the RAS (Right-Anterior-Superior) orientation,
and the rotations are applied following the right-hand rule (counter-clockwise):

```math
\begin{align*}
pvillacorta marked this conversation as resolved.
Show resolved Hide resolved
ux &= cos(\gamma)cos(\beta)x \\
&+ (cos(\gamma)sin(\beta)sin(\alpha) - sin(\gamma)cos(\alpha))y\\
&+ (cos(\gamma)sin(\beta)cos(\alpha) + sin(\gamma)sin(\alpha))z\\
&- x
\end{align*}
```

```math
\begin{align*}
uy &= sin(\gamma)cos(\beta)x \\
&+ (sin(\gamma)sin(\beta)sin(\alpha) + cos(\gamma)cos(\alpha))y\\
&+ (sin(\gamma)sin(\beta)cos(\alpha) - cos(\gamma)sin(\alpha))z\\
&- y
\end{align*}
```
![Head Rotation Axis](../assets/head-rotation-axis.svg)

The applied rotation matrix is obtained as follows:
```math
\begin{align*}
uz &= -sin(\beta)x \\
&+ cos(\beta)sin(\alpha)y\\
&+ cos(\beta)cos(\alpha)z\\
&- z
\end{align*}
```

where:

```math
\alpha = \left\{\begin{matrix}
0, & t <= t_start \\
\frac{pitch}{t_end-t_start}(t-t_start), & t_start < t < t_end \\
pitch, & t >= t_end
\end{matrix}\right.
,\qquad
\beta = \left\{\begin{matrix}
0, & t <= t_start \\
\frac{roll}{t_end-t_start}(t-t_start), & t_start < t < t_end \\
roll, & t >= t_end
\end{matrix}\right.
,\qquad
\gamma = \left\{\begin{matrix}
0, & t <= t_start \\
\frac{yaw}{t_end-t_start}(t-t_start), & t_start < t < t_end \\
yaw, & t >= t_end
\end{matrix}\right.
\begin{equation}
\begin{aligned}
R &= R_z(\alpha) R_y(\beta) R_x(\gamma) \\
&= \begin{bmatrix}
\cos \alpha & -\sin \alpha & 0 \\
\sin \alpha & \cos \alpha & 0 \\
0 & 0 & 1
\end{bmatrix}
\begin{bmatrix}
\cos \beta & 0 & \sin \beta \\
0 & 1 & 0 \\
-\sin \beta & 0 & \cos \beta
\end{bmatrix}
\begin{bmatrix}
1 & 0 & 0 \\
0 & \cos \gamma & -\sin \gamma \\
0 & \sin \gamma & \cos \gamma
\end{bmatrix} \\
&= \begin{bmatrix}
\cos \alpha \cos \beta & \cos \alpha \sin \beta \sin \gamma - \sin \alpha \cos \gamma & \cos \alpha \sin \beta \cos \gamma + \sin \alpha \sin \gamma \\
\sin \alpha \cos \beta & \sin \alpha \sin \beta \sin \gamma + \cos \alpha \cos \gamma & \sin \alpha \sin \beta \cos \gamma - \cos \alpha \sin \gamma \\
-\sin \beta & \cos \beta \sin \gamma & \cos \beta \cos \gamma
\end{bmatrix}
\end{aligned}
\end{equation}
```

# Arguments
Expand All @@ -63,6 +47,10 @@ yaw, & t >= t_end
# Returns
- `rotation`: (`::Rotation`) Rotation struct

# Examples
```julia-repl
julia> rt = Rotation(pitch=15.0, roll=0.0, yaw=20.0, t_start=0.1, t_end=0.5)
```
"""
@with_kw struct Rotation{T<:Real} <: SimpleMotionType{T}
pitch :: T
Expand All @@ -83,12 +71,12 @@ function displacement_x(
t::AbstractArray{T},
) where {T<:Real}
t_unit = unit_time(t, motion_type.t_start, motion_type.t_end)
α = t_unit .* (motion_type.pitch)
α = t_unit .* (motion_type.yaw)
β = t_unit .* (motion_type.roll)
γ = t_unit .* (motion_type.yaw)
return cosd.(γ) .* cosd.(β) .* x +
(cosd.(γ) .* sind.(β) .* sind.(α) .- sind.(γ) .* cosd.(α)) .* y +
(cosd.(γ) .* sind.(β) .* cosd.(α) .+ sind.(γ) .* sind.(α)) .* z .- x
γ = t_unit .* (motion_type.pitch)
return cosd.(α) .* cosd.(β) .* x +
(cosd.(α) .* sind.(β) .* sind.(γ) .- sind.(α) .* cosd.(γ)) .* y +
(cosd.(α) .* sind.(β) .* cosd.(γ) .+ sind.(α) .* sind.(γ)) .* z .- x
end

function displacement_y(
Expand All @@ -99,12 +87,12 @@ function displacement_y(
t::AbstractArray{T},
) where {T<:Real}
t_unit = unit_time(t, motion_type.t_start, motion_type.t_end)
α = t_unit .* motion_type.pitch
β = t_unit .* motion_type.roll
γ = t_unit .* motion_type.yaw
return sind.(γ) .* cosd.(β) .* x +
(sind.(γ) .* sind.(β) .* sind.(α) .+ cosd.(γ) .* cosd.(α)) .* y +
(sind.(γ) .* sind.(β) .* cosd.(α) .- cosd.(γ) .* sind.(α)) .* z .- y
α = t_unit .* (motion_type.yaw)
β = t_unit .* (motion_type.roll)
γ = t_unit .* (motion_type.pitch)
return sind.(α) .* cosd.(β) .* x +
(sind.(α) .* sind.(β) .* sind.(γ) .+ cosd.(α) .* cosd.(γ)) .* y +
(sind.(α) .* sind.(β) .* cosd.(γ) .- cosd.(α) .* sind.(γ)) .* z .- y
end

function displacement_z(
Expand All @@ -115,10 +103,12 @@ function displacement_z(
t::AbstractArray{T},
) where {T<:Real}
t_unit = unit_time(t, motion_type.t_start, motion_type.t_end)
α = t_unit .* motion_type.pitch
β = t_unit .* motion_type.roll
γ = t_unit .* motion_type.yaw
return -sind.(β) .* x + cosd.(β) .* sind.(α) .* y .- z
α = t_unit .* (motion_type.yaw)
β = t_unit .* (motion_type.roll)
γ = t_unit .* (motion_type.pitch)
return -sind.(β) .* x +
cosd.(β) .* sind.(γ) .* y +
cosd.(β) .* cosd.(γ) .* z .- z
end

times(motion_type::Rotation) = begin
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,9 @@
@doc raw"""
translation = Translation(t_start, t_end, dx, dy, dz)

Translation motion struct. It produces a translation of the phantom in the three directions x, y and z.

```math
ux=\left\{\begin{matrix}
0, & t <= t_start\\
\frac{dx}{t_end-t_start}(t-t_start), & t_start < t < t_end\\
dx, & t >= t_end
\end{matrix}\right.
```

```math
uy=\left\{\begin{matrix}
0, & t <= t_start\\
\frac{dy}{t_end-t_start}(t-t_start), & t_start < t < t_end\\
dy, & t >= t_end
\end{matrix}\right.
```

```math
uz=\left\{\begin{matrix}
0, & t <= t_start\\
\frac{dz}{t_end-t_start}(t-t_start), & t_start < t < t_end\\
dz, & t >= t_end
\end{matrix}\right.
```
Translation motion struct. It produces a linear translation of the phantom.
Its fields are the final displacements in the three axes (dx, dy, dz)
and the start and end times of the translation.

# Arguments
- `dx`: (`::Real`, `[m]`) translation in x
Expand All @@ -37,6 +15,10 @@ dz, & t >= t_end
# Returns
- `translation`: (`::Translation`) Translation struct

# Examples
```julia-repl
julia> tr = Translation(dx=0.01, dy=0.02, dz=0.03, t_start=0.0, t_end=0.5)
```
"""
@with_kw struct Translation{T<:Real} <: SimpleMotionType{T}
dx :: T
Expand Down
Loading