From 65d76dcc3c8c76aa8b872af92735ecc3a783befe Mon Sep 17 00:00:00 2001
From: Michiel Cottaar <michiel.cottaar@ndcn.ox.ac.uk>
Date: Tue, 20 Feb 2024 18:53:57 +0000
Subject: [PATCH] Treat pathway values as variables

---
 src/MRIBuilder.jl |  4 ++--
 src/pathways.jl   | 61 ++++++++++++++++++++++++++++++++++-------------
 2 files changed, 46 insertions(+), 19 deletions(-)

diff --git a/src/MRIBuilder.jl b/src/MRIBuilder.jl
index cf2f428..ec1160c 100644
--- a/src/MRIBuilder.jl
+++ b/src/MRIBuilder.jl
@@ -27,8 +27,8 @@ export InstantPulse, ConstantPulse, SincPulse, GenericPulse, InstantGradient, Si
 import .Containers: ContainerBlock, start_time, end_time, waveform, waveform_sequence, events, BaseBuildingBlock, BuildingBlock, Wait, BaseSequence, nrepeat, Sequence, AlternativeBlocks, match_blocks!, get_index_single_TR
 export ContainerBlock, start_time, end_time, waveform, waveform_sequence, events, BaseBuildingBlock, BuildingBlock, Wait, BaseSequence, nrepeat, Sequence, AlternativeBlocks, match_blocks!, get_index_single_TR
 
-import .Pathways: Pathway, duration_transverse, duration_dephase, bval, bmat
-export Pathway, duration_transverse, duration_dephase, bval, bmat
+import .Pathways: Pathway, duration_transverse, duration_dephase, bval, bmat, get_pathway
+export Pathway, duration_transverse, duration_dephase, bval, bmat, get_pathway
 
 import .Parts: excitation_pulse, refocus_pulse, epi_readout, single_line_readout, Trapezoid, SliceSelect, LineReadout, opposite_kspace_lines, SpoiltSliceSelect, SliceSelectRephase, EPIReadout
 export excitation_pulse, refocus_pulse, epi_readout, single_line_readout, Trapezoid, SliceSelect, LineReadout, opposite_kspace_lines, SpoiltSliceSelect, SliceSelectRephase, EPIReadout
diff --git a/src/pathways.jl b/src/pathways.jl
index 1724950..3e020fe 100644
--- a/src/pathways.jl
+++ b/src/pathways.jl
@@ -3,7 +3,7 @@ import LinearAlgebra: norm, tr
 import StaticArrays: SVector, SMatrix
 import ..Components: NoGradient, RFPulseComponent, ReadoutComponent, InstantGradient, GradientWaveform, DelayedEvent
 import ..Containers: BaseSequence, Sequence, BaseBuildingBlock, waveform, events, waveform_sequence, start_time, AlternativeBlocks
-import ..Variables: qvec, qval, bmat_gradient, VariableType, effective_time, duration, TR
+import ..Variables: qvec, qval, bmat_gradient, VariableType, effective_time, duration, TR, variables
 
 
 """
@@ -112,54 +112,81 @@ end
 
 
 """
-    qvec(pathway::Pathway; group=nothing)
+    qvec(pathway::Pathway)
 
 Return net displacement vector in k-space/q-space experienced by the spins following a specific [`Pathway`](@ref).
 
 Only gradients active while the spins are in the transverse plane are considered.
 
-By default gradients that have been added to a specific `group` are ignored.
-You can set `group=<name>` to consider a specific `group.
+Returns a NamedTuple with the `qvec` for all gradient groups.
 """
-qvec(pathway::Pathway; group=nothing) = get(pathway.qvec, group, zero(SVector{3, Float64}))
+qvec(pathway::Pathway, group) = pathway.qvec[group]
 
 
 """
-    area_under_curve(pathway::Pathway; group=nothing)
+    area_under_curve(pathway::Pathway)
 
 Return net displacement in k-space (i.e., spoiling) experienced by the spins following a specific [`Pathway`](@ref).
 
 Only gradients active while the spins are in the transverse plane are considered.
 
-By default gradients that have been added to a specific `group` are ignored.
-You can set `group=<name>` to consider a specific `group.
+Returns a NamedTuple with the `area_under_curve` for all gradient groups.
 """
-area_under_curve(pathway::Pathway; group=nothing) = qval(pathway; group=group)
+area_under_curve(pathway::Pathway, group) = norm(qvec(pathway, group))
 
 
 """
-    bmat(pathway::Pathway; group=nothing)
+    bmat(pathway::Pathway)
 
 Return 3x3 diffusion-weighted matrix experienced by the spins following a specific [`Pathway`](@ref) in rad^2 ms/um^2.
 
 Only gradients active while the spins are in the transverse plane are considered.
 
-By default gradients that have been added to a specific `group` are ignored.
-You can set `group=<name>` to consider a specific `group.
+Returns a NamedTuple with the `bmat` for all gradient groups.
 """
-bmat(pathway::Pathway; group=nothing)  = get(pathway.bmat, group, zero(SMatrix{3, 3, Float64, 9}))
+bmat(pathway::Pathway, group)  = pathway.bmat[group]
 
 """
-    bval(pathway::Pathway; group=nothing)
+    bval(pathway::Pathway)
 
 Return size of diffusion-weighting experienced by the spins following a specific [`Pathway`](@ref) in rad^2 ms/um^2.
 
 Only gradients active while the spins are in the transverse plane will contribute to the diffusion weighting.
 
-By default gradients that have been added to a specific `group` are ignored.
-You can set `group=<name>` to consider a specific `group.
+Returns a NamedTuple with the `bval` for all gradient groups.
 """
-bval(pathway::Pathway; group=nothing) = tr(bmat(pathway; group=group))
+bval(pathway::Pathway, group) = tr(bmat(pathway, group))
+
+
+"""
+    get_pathway(sequence)
+
+Gets the main [`PathWay`](@ref) that spins are expected to experience in the sequence.
+
+Multiple pathways might be returned as an array or (named)tuple.
+"""
+function get_pathway end
+
+for fn in (:qvec, :area_under_curve, :bmat, :bval)
+    @eval function $fn(pathway::Pathway)
+        return NamedTuple(group => $fn(pathway, group) for group in keys(pathway.qvec))
+    end
+end
+
+for fn in (:qvec, :area_under_curve, :bmat, :bval, :duration_dephase, :duration_transverse)
+    @eval function $fn(seq::Sequence)
+        pathway = get_pathway(seq)
+        if pathway isa Pathway
+            return $fn(pathway)
+        elseif pathway isa AbstractVector || pathway isa Tuple
+            return $fn.(pathway)
+        elseif pathway isa NumedTuple
+            return NamedTuple(k => $fn(v) for (k, v) in pairs(pathway))
+        end
+        error("get_pathway returned unexpected type for $seq")
+    end
+    @eval variables[$(QuoteNode(fn))] = $fn
+end
 
 
 """
-- 
GitLab